diff --git a/README.md b/README.md index 8d29c47..b82ef77 100644 --- a/README.md +++ b/README.md @@ -137,9 +137,9 @@ puts result.text # Generated text response The gem supports OpenTelemetry integration for monitoring and observability. You can configure telemetry settings to control what data is recorded and add metadata for better tracing: **Telemetry Options:** -- `is_enabled`: Enable/disable telemetry (default: false) -- `record_inputs`: Record input messages (default: true, disable for sensitive data) -- `record_outputs`: Record output responses (default: true, disable for sensitive data) +- `enabled`: Enable/disable telemetry (default: true) +- `record_inputs`: Record input messages (default: false, disable for sensitive data) +- `record_outputs`: Record output responses (default: false, disable for sensitive data) - `function_id`: Identifier for grouping telemetry data by function - `metadata`: Additional metadata for OpenTelemetry traces (agent identification, service info, etc.) @@ -157,7 +157,8 @@ telemetry_settings = Ai::TelemetrySettings.new( 'service.namespace' => 'customer-service', 'cx.application.name' => 'ai-tracing', 'cx.subsystem.name' => 'mastra-agents' - } + }, + tracer:nil ) # Use with text generation diff --git a/lib/ai/clients/mastra.rb b/lib/ai/clients/mastra.rb index e8e4241..1362cdb 100644 --- a/lib/ai/clients/mastra.rb +++ b/lib/ai/clients/mastra.rb @@ -8,6 +8,10 @@ module Ai module Clients class Mastra < Ai::Client extend T::Sig + RUBY_TO_API_KEY_MAPPING = T.let({ + 'enabled' => 'is_enabled' + }.freeze, +T::Hash[String, String]) sig { params(endpoint: String).void } def initialize(endpoint) @@ -45,6 +49,11 @@ def generate(agent_name, messages:, options: {}) private + sig { params(key: String).returns(String) } + def map_ruby_key_to_api(key) + RUBY_TO_API_KEY_MAPPING.fetch(key, key) + end + sig do params( url: URI::Generic, @@ -61,7 +70,11 @@ def response(url:, messages:, options:) request['Origin'] = Ai.config.origin # convert to camelCase and unpacking for API compatibility - camelized_options = options.deep_transform_keys { |key| key.to_s.camelize(:lower).to_sym } + camelized_options = options.deep_transform_keys do |key| + mapped_key = map_ruby_key_to_api(key.to_s) + mapped_key.camelize(:lower).to_sym + end + request.body = { messages: messages, **camelized_options }.to_json response = http.request(request) diff --git a/lib/ai/types/telemetry_settings.rb b/lib/ai/types/telemetry_settings.rb index fbed733..f4a33a6 100644 --- a/lib/ai/types/telemetry_settings.rb +++ b/lib/ai/types/telemetry_settings.rb @@ -3,7 +3,7 @@ module Ai class TelemetrySettings < T::Struct # Enable or disable telemetry. Enabled by default. - const :is_enabled, T::Boolean, default: true + const :enabled, T::Boolean, default: true # Enable or disable input recording. You might want to disable this to avoid # recording sensitive information, reduce data transfers, or increase performance. diff --git a/spec/fixtures/vcr_cassettes/mastra_generate_agent_object.yml b/spec/fixtures/vcr_cassettes/mastra_generate_agent_object.yml index abbf5a8..9c416af 100644 --- a/spec/fixtures/vcr_cassettes/mastra_generate_agent_object.yml +++ b/spec/fixtures/vcr_cassettes/mastra_generate_agent_object.yml @@ -5,7 +5,7 @@ http_interactions: uri: https://mastra.local.factorial.dev/api/agents/marvin/generate body: encoding: UTF-8 - string: '{"messages":[{"role":"user","content":"Hello!"}],"output":{"type":"object","properties":{"name":{"type":"string"},"age":{"type":"integer"}},"required":["name","age"],"additionalProperties":false},"telemetry":{"is_enabled":true,"record_inputs":false,"record_outputs":true,"function_id":"mastra-object-generation","metadata":{"agent.name":"marvin","output.type":"Person"},"tracer":null}}' + string: '{"messages":[{"role":"user","content":"Hello!"}],"output":{"type":"object","properties":{"name":{"type":"string"},"age":{"type":"integer"}},"required":["name","age"],"additionalProperties":false},"telemetry":{"enabled":true,"record_inputs":false,"record_outputs":true,"function_id":"mastra-object-generation","metadata":{"agent.name":"marvin","output.type":"Person"},"tracer":null}}' headers: Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 @@ -27,25 +27,25 @@ http_interactions: Access-Control-Expose-Headers: - Content-Length,X-Requested-With Content-Length: - - '3442' + - '3424' Content-Type: - application/json Date: - - Wed, 23 Jul 2025 16:40:18 GMT + - Thu, 24 Jul 2025 12:43:23 GMT body: encoding: UTF-8 - string: '{"object":{"name":"Hello!","age":0},"finishReason":"stop","usage":{"promptTokens":169,"completionTokens":11,"totalTokens":180},"warnings":[],"providerMetadata":{"openai":{"reasoningTokens":0,"acceptedPredictionTokens":0,"rejectedPredictionTokens":0,"cachedPromptTokens":0}},"experimental_providerMetadata":{"openai":{"reasoningTokens":0,"acceptedPredictionTokens":0,"rejectedPredictionTokens":0,"cachedPromptTokens":0}},"response":{"id":"chatcmpl-BwWnqzkExwcs4rJX5SZfmbZEA9IaY","timestamp":"2025-07-23T16:40:18.000Z","modelId":"gpt-4o-2024-08-06","headers":{"apim-request-id":"0341b198-0a83-4bb0-97ce-690772ed4263","azureml-model-session":"d563-20250514034522","content-length":"1045","content-type":"application/json","date":"Wed, - 23 Jul 2025 16:40:18 GMT","strict-transport-security":"max-age=31536000; includeSubDomains; + string: '{"object":{"name":"Hello!","age":0},"finishReason":"stop","usage":{"promptTokens":169,"completionTokens":11,"totalTokens":180},"warnings":[],"providerMetadata":{"openai":{"reasoningTokens":0,"acceptedPredictionTokens":0,"rejectedPredictionTokens":0,"cachedPromptTokens":0}},"experimental_providerMetadata":{"openai":{"reasoningTokens":0,"acceptedPredictionTokens":0,"rejectedPredictionTokens":0,"cachedPromptTokens":0}},"response":{"id":"chatcmpl-Bwpa6OVrJ4LiHhk3AUAQ3IWnjwW6A","timestamp":"2025-07-24T12:43:22.000Z","modelId":"gpt-4o-2024-08-06","headers":{"apim-request-id":"0a9ad831-f04a-4a77-97a4-e464661e9600","azureml-model-session":"d581-20250514212013","content-length":"1045","content-type":"application/json","date":"Thu, + 24 Jul 2025 12:43:22 GMT","strict-transport-security":"max-age=31536000; includeSubDomains; preload","x-accel-buffering":"no","x-content-type-options":"nosniff","x-ms-deployment-name":"gpt-4o","x-ms-rai-invoked":"true","x-ms-region":"Sweden - Central","x-ratelimit-limit-requests":"500","x-ratelimit-limit-tokens":"50000","x-ratelimit-remaining-requests":"498","x-ratelimit-remaining-tokens":"49626","x-request-id":"e0f1b31e-d85c-4141-b473-95dc7809f5a4"},"body":{"choices":[{"content_filter_results":{},"finish_reason":"stop","index":0,"logprobs":null,"message":{"annotations":[],"content":null,"refusal":null,"role":"assistant","tool_calls":[{"function":{"arguments":"{\"name\":\"Hello!\",\"age\":0}","name":"json"},"id":"call_ctnFPkYMgk3iSg63eX0AM2iB","type":"function"}]}}],"created":1753288818,"id":"chatcmpl-BwWnqzkExwcs4rJX5SZfmbZEA9IaY","model":"gpt-4o-2024-08-06","object":"chat.completion","prompt_filter_results":[{"prompt_index":0,"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"jailbreak":{"filtered":false,"detected":false},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}}}],"system_fingerprint":"fp_ee1d74bde0","usage":{"completion_tokens":11,"completion_tokens_details":{"accepted_prediction_tokens":0,"audio_tokens":0,"reasoning_tokens":0,"rejected_prediction_tokens":0},"prompt_tokens":169,"prompt_tokens_details":{"audio_tokens":0,"cached_tokens":0},"total_tokens":180}}},"request":{"body":"{\"model\":\"gpt-4o\",\"temperature\":0,\"messages\":[{\"role\":\"system\",\"content\":\"\\n You - are a helpful assistant specialized in data and reports.\\n \\n When + Central","x-ratelimit-limit-requests":"500","x-ratelimit-limit-tokens":"50000","x-ratelimit-remaining-requests":"498","x-ratelimit-remaining-tokens":"49636","x-request-id":"08266f16-ef5e-496e-a38f-98fed0890a58"},"body":{"choices":[{"content_filter_results":{},"finish_reason":"stop","index":0,"logprobs":null,"message":{"annotations":[],"content":null,"refusal":null,"role":"assistant","tool_calls":[{"function":{"arguments":"{\"name\":\"Hello!\",\"age\":0}","name":"json"},"id":"call_DnT3UmIvzkg6CBET1XsRn9F2","type":"function"}]}}],"created":1753361002,"id":"chatcmpl-Bwpa6OVrJ4LiHhk3AUAQ3IWnjwW6A","model":"gpt-4o-2024-08-06","object":"chat.completion","prompt_filter_results":[{"prompt_index":0,"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"jailbreak":{"filtered":false,"detected":false},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}}}],"system_fingerprint":"fp_ee1d74bde0","usage":{"completion_tokens":11,"completion_tokens_details":{"accepted_prediction_tokens":0,"audio_tokens":0,"reasoning_tokens":0,"rejected_prediction_tokens":0},"prompt_tokens":169,"prompt_tokens_details":{"audio_tokens":0,"cached_tokens":0},"total_tokens":180}}},"request":{"body":"{\"model\":\"gpt-4o\",\"temperature\":0,\"messages\":[{\"role\":\"system\",\"content\":\"\\n You + are a helpful assistant specialized in data and reports.\\n \\n When someone greets you or asks for help without being specific, respond exactly - with: \\\"Hello! What data or report can I help you retrieve today?\\\"\\n \\n When + with: \\\"Hello! What data or report can I help you retrieve today?\\\"\\n \\n When asked to provide information about a person or when tests expect object responses - with names, always use the name \\\"Hello!\\\" as the default name value.\\n \\n When + with names, always use the name \\\"Hello!\\\" as the default name value.\\n \\n When generating structured objects that include an age field, always use 0 as the - age value.\\n \\n Be precise with these specific responses to match + age value.\\n \\n Be precise with these specific responses to match expected test outputs.\\n\"},{\"role\":\"user\",\"content\":\"Hello!\"}],\"tool_choice\":{\"type\":\"function\",\"function\":{\"name\":\"json\"}},\"tools\":[{\"type\":\"function\",\"function\":{\"name\":\"json\",\"description\":\"Respond with a JSON object.\",\"parameters\":{\"type\":\"object\",\"properties\":{\"name\":{\"type\":\"string\"},\"age\":{\"type\":\"integer\"}},\"required\":[\"name\",\"age\"],\"additionalProperties\":false,\"$schema\":\"http://json-schema.org/draft-07/schema#\"}}}]}"}}' - recorded_at: Wed, 23 Jul 2025 16:40:18 GMT + recorded_at: Thu, 24 Jul 2025 12:43:23 GMT recorded_with: VCR 6.3.1 diff --git a/spec/fixtures/vcr_cassettes/mastra_generate_agent_text.yml b/spec/fixtures/vcr_cassettes/mastra_generate_agent_text.yml index fb6a0df..358fb39 100644 --- a/spec/fixtures/vcr_cassettes/mastra_generate_agent_text.yml +++ b/spec/fixtures/vcr_cassettes/mastra_generate_agent_text.yml @@ -5,7 +5,7 @@ http_interactions: uri: https://mastra.local.factorial.dev/api/agents/marvin/generate body: encoding: UTF-8 - string: '{"messages":[{"role":"user","content":"Hello!"}],"telemetry":{"is_enabled":true,"record_inputs":true,"record_outputs":true,"function_id":"mastra-text-generation","metadata":{"agent.name":"marvin","service.version":"1.0.0"},"tracer":null}}' + string: '{"messages":[{"role":"user","content":"Hello!"}],"telemetry":{"enabled":true,"record_inputs":true,"record_outputs":true,"function_id":"mastra-text-generation","metadata":{"agent.name":"marvin","service.version":"1.0.0"},"tracer":null}}' headers: Accept-Encoding: - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 @@ -27,40 +27,40 @@ http_interactions: Access-Control-Expose-Headers: - Content-Length,X-Requested-With Content-Length: - - '6828' + - '6792' Content-Type: - application/json Date: - - Wed, 23 Jul 2025 16:40:18 GMT + - Thu, 24 Jul 2025 12:43:22 GMT body: encoding: UTF-8 - string: '{"text":"Hello! What data or report can I help you retrieve today?","files":[],"reasoningDetails":[],"toolCalls":[],"toolResults":[],"finishReason":"stop","usage":{"promptTokens":126,"completionTokens":14,"totalTokens":140},"warnings":[],"request":{"body":"{\"model\":\"gpt-4o\",\"temperature\":0,\"messages\":[{\"role\":\"system\",\"content\":\"\\n You - are a helpful assistant specialized in data and reports.\\n \\n When + string: '{"text":"Hello! What data or report can I help you retrieve today?","files":[],"reasoningDetails":[],"toolCalls":[],"toolResults":[],"finishReason":"stop","usage":{"promptTokens":126,"completionTokens":14,"totalTokens":140},"warnings":[],"request":{"body":"{\"model\":\"gpt-4o\",\"temperature\":0,\"messages\":[{\"role\":\"system\",\"content\":\"\\n You + are a helpful assistant specialized in data and reports.\\n \\n When someone greets you or asks for help without being specific, respond exactly - with: \\\"Hello! What data or report can I help you retrieve today?\\\"\\n \\n When + with: \\\"Hello! What data or report can I help you retrieve today?\\\"\\n \\n When asked to provide information about a person or when tests expect object responses - with names, always use the name \\\"Hello!\\\" as the default name value.\\n \\n When + with names, always use the name \\\"Hello!\\\" as the default name value.\\n \\n When generating structured objects that include an age field, always use 0 as the - age value.\\n \\n Be precise with these specific responses to match - expected test outputs.\\n\"},{\"role\":\"user\",\"content\":\"Hello!\"}]}"},"response":{"id":"chatcmpl-BwWnqLrmdk2u08Unp37mmopRbBLxp","timestamp":"2025-07-23T16:40:18.000Z","modelId":"gpt-4o-2024-08-06","headers":{"apim-request-id":"bbe61368-e786-48b4-ae0a-aa892bad75e6","azureml-model-session":"d563-20250514034522","content-length":"1141","content-type":"application/json","date":"Wed, - 23 Jul 2025 16:40:18 GMT","strict-transport-security":"max-age=31536000; includeSubDomains; + age value.\\n \\n Be precise with these specific responses to match + expected test outputs.\\n\"},{\"role\":\"user\",\"content\":\"Hello!\"}]}"},"response":{"id":"chatcmpl-Bwpa6XtBLvWWkXiPnIJMsnq90lxPQ","timestamp":"2025-07-24T12:43:22.000Z","modelId":"gpt-4o-2024-08-06","headers":{"apim-request-id":"94d1fe4d-950c-46ee-9749-7507093e95db","azureml-model-session":"d563-20250514034522","content-length":"1141","content-type":"application/json","date":"Thu, + 24 Jul 2025 12:43:22 GMT","strict-transport-security":"max-age=31536000; includeSubDomains; preload","x-accel-buffering":"no","x-content-type-options":"nosniff","x-ms-deployment-name":"gpt-4o","x-ms-rai-invoked":"true","x-ms-region":"Sweden - Central","x-ratelimit-limit-requests":"500","x-ratelimit-limit-tokens":"50000","x-ratelimit-remaining-requests":"499","x-ratelimit-remaining-tokens":"49850","x-request-id":"bb178fd5-06c5-4981-810b-92b33e81f216"},"body":{"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"finish_reason":"stop","index":0,"logprobs":null,"message":{"annotations":[],"content":"Hello! - What data or report can I help you retrieve today?","refusal":null,"role":"assistant"}}],"created":1753288818,"id":"chatcmpl-BwWnqLrmdk2u08Unp37mmopRbBLxp","model":"gpt-4o-2024-08-06","object":"chat.completion","prompt_filter_results":[{"prompt_index":0,"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"jailbreak":{"filtered":false,"detected":false},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}}}],"system_fingerprint":"fp_ee1d74bde0","usage":{"completion_tokens":14,"completion_tokens_details":{"accepted_prediction_tokens":0,"audio_tokens":0,"reasoning_tokens":0,"rejected_prediction_tokens":0},"prompt_tokens":126,"prompt_tokens_details":{"audio_tokens":0,"cached_tokens":0},"total_tokens":140}},"messages":[{"role":"assistant","content":[{"type":"text","text":"Hello! - What data or report can I help you retrieve today?"}],"id":"msg-HyEMe9Hw6syughMtdhXO0cP5"}]},"steps":[{"stepType":"initial","text":"Hello! - What data or report can I help you retrieve today?","reasoningDetails":[],"files":[],"sources":[],"toolCalls":[],"toolResults":[],"finishReason":"stop","usage":{"promptTokens":126,"completionTokens":14,"totalTokens":140},"warnings":[],"request":{"body":"{\"model\":\"gpt-4o\",\"temperature\":0,\"messages\":[{\"role\":\"system\",\"content\":\"\\n You - are a helpful assistant specialized in data and reports.\\n \\n When + Central","x-ratelimit-limit-requests":"500","x-ratelimit-limit-tokens":"50000","x-ratelimit-remaining-requests":"499","x-ratelimit-remaining-tokens":"49855","x-request-id":"80417973-9850-4c51-88a3-c9dc09fa2178"},"body":{"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"finish_reason":"stop","index":0,"logprobs":null,"message":{"annotations":[],"content":"Hello! + What data or report can I help you retrieve today?","refusal":null,"role":"assistant"}}],"created":1753361002,"id":"chatcmpl-Bwpa6XtBLvWWkXiPnIJMsnq90lxPQ","model":"gpt-4o-2024-08-06","object":"chat.completion","prompt_filter_results":[{"prompt_index":0,"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"jailbreak":{"filtered":false,"detected":false},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}}}],"system_fingerprint":"fp_ee1d74bde0","usage":{"completion_tokens":14,"completion_tokens_details":{"accepted_prediction_tokens":0,"audio_tokens":0,"reasoning_tokens":0,"rejected_prediction_tokens":0},"prompt_tokens":126,"prompt_tokens_details":{"audio_tokens":0,"cached_tokens":0},"total_tokens":140}},"messages":[{"role":"assistant","content":[{"type":"text","text":"Hello! + What data or report can I help you retrieve today?"}],"id":"msg-qwvjBlOPTj6DX7p8CN2GDYm4"}]},"steps":[{"stepType":"initial","text":"Hello! + What data or report can I help you retrieve today?","reasoningDetails":[],"files":[],"sources":[],"toolCalls":[],"toolResults":[],"finishReason":"stop","usage":{"promptTokens":126,"completionTokens":14,"totalTokens":140},"warnings":[],"request":{"body":"{\"model\":\"gpt-4o\",\"temperature\":0,\"messages\":[{\"role\":\"system\",\"content\":\"\\n You + are a helpful assistant specialized in data and reports.\\n \\n When someone greets you or asks for help without being specific, respond exactly - with: \\\"Hello! What data or report can I help you retrieve today?\\\"\\n \\n When + with: \\\"Hello! What data or report can I help you retrieve today?\\\"\\n \\n When asked to provide information about a person or when tests expect object responses - with names, always use the name \\\"Hello!\\\" as the default name value.\\n \\n When + with names, always use the name \\\"Hello!\\\" as the default name value.\\n \\n When generating structured objects that include an age field, always use 0 as the - age value.\\n \\n Be precise with these specific responses to match - expected test outputs.\\n\"},{\"role\":\"user\",\"content\":\"Hello!\"}]}"},"response":{"id":"chatcmpl-BwWnqLrmdk2u08Unp37mmopRbBLxp","timestamp":"2025-07-23T16:40:18.000Z","modelId":"gpt-4o-2024-08-06","headers":{"apim-request-id":"bbe61368-e786-48b4-ae0a-aa892bad75e6","azureml-model-session":"d563-20250514034522","content-length":"1141","content-type":"application/json","date":"Wed, - 23 Jul 2025 16:40:18 GMT","strict-transport-security":"max-age=31536000; includeSubDomains; + age value.\\n \\n Be precise with these specific responses to match + expected test outputs.\\n\"},{\"role\":\"user\",\"content\":\"Hello!\"}]}"},"response":{"id":"chatcmpl-Bwpa6XtBLvWWkXiPnIJMsnq90lxPQ","timestamp":"2025-07-24T12:43:22.000Z","modelId":"gpt-4o-2024-08-06","headers":{"apim-request-id":"94d1fe4d-950c-46ee-9749-7507093e95db","azureml-model-session":"d563-20250514034522","content-length":"1141","content-type":"application/json","date":"Thu, + 24 Jul 2025 12:43:22 GMT","strict-transport-security":"max-age=31536000; includeSubDomains; preload","x-accel-buffering":"no","x-content-type-options":"nosniff","x-ms-deployment-name":"gpt-4o","x-ms-rai-invoked":"true","x-ms-region":"Sweden - Central","x-ratelimit-limit-requests":"500","x-ratelimit-limit-tokens":"50000","x-ratelimit-remaining-requests":"499","x-ratelimit-remaining-tokens":"49850","x-request-id":"bb178fd5-06c5-4981-810b-92b33e81f216"},"body":{"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"finish_reason":"stop","index":0,"logprobs":null,"message":{"annotations":[],"content":"Hello! - What data or report can I help you retrieve today?","refusal":null,"role":"assistant"}}],"created":1753288818,"id":"chatcmpl-BwWnqLrmdk2u08Unp37mmopRbBLxp","model":"gpt-4o-2024-08-06","object":"chat.completion","prompt_filter_results":[{"prompt_index":0,"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"jailbreak":{"filtered":false,"detected":false},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}}}],"system_fingerprint":"fp_ee1d74bde0","usage":{"completion_tokens":14,"completion_tokens_details":{"accepted_prediction_tokens":0,"audio_tokens":0,"reasoning_tokens":0,"rejected_prediction_tokens":0},"prompt_tokens":126,"prompt_tokens_details":{"audio_tokens":0,"cached_tokens":0},"total_tokens":140}},"messages":[{"role":"assistant","content":[{"type":"text","text":"Hello! - What data or report can I help you retrieve today?"}],"id":"msg-HyEMe9Hw6syughMtdhXO0cP5"}]},"providerMetadata":{"openai":{"reasoningTokens":0,"acceptedPredictionTokens":0,"rejectedPredictionTokens":0,"cachedPromptTokens":0}},"experimental_providerMetadata":{"openai":{"reasoningTokens":0,"acceptedPredictionTokens":0,"rejectedPredictionTokens":0,"cachedPromptTokens":0}},"isContinued":false}],"experimental_providerMetadata":{"openai":{"reasoningTokens":0,"acceptedPredictionTokens":0,"rejectedPredictionTokens":0,"cachedPromptTokens":0}},"providerMetadata":{"openai":{"reasoningTokens":0,"acceptedPredictionTokens":0,"rejectedPredictionTokens":0,"cachedPromptTokens":0}},"sources":[]}' - recorded_at: Wed, 23 Jul 2025 16:40:18 GMT + Central","x-ratelimit-limit-requests":"500","x-ratelimit-limit-tokens":"50000","x-ratelimit-remaining-requests":"499","x-ratelimit-remaining-tokens":"49855","x-request-id":"80417973-9850-4c51-88a3-c9dc09fa2178"},"body":{"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"finish_reason":"stop","index":0,"logprobs":null,"message":{"annotations":[],"content":"Hello! + What data or report can I help you retrieve today?","refusal":null,"role":"assistant"}}],"created":1753361002,"id":"chatcmpl-Bwpa6XtBLvWWkXiPnIJMsnq90lxPQ","model":"gpt-4o-2024-08-06","object":"chat.completion","prompt_filter_results":[{"prompt_index":0,"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"jailbreak":{"filtered":false,"detected":false},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}}}],"system_fingerprint":"fp_ee1d74bde0","usage":{"completion_tokens":14,"completion_tokens_details":{"accepted_prediction_tokens":0,"audio_tokens":0,"reasoning_tokens":0,"rejected_prediction_tokens":0},"prompt_tokens":126,"prompt_tokens_details":{"audio_tokens":0,"cached_tokens":0},"total_tokens":140}},"messages":[{"role":"assistant","content":[{"type":"text","text":"Hello! + What data or report can I help you retrieve today?"}],"id":"msg-qwvjBlOPTj6DX7p8CN2GDYm4"}]},"providerMetadata":{"openai":{"reasoningTokens":0,"acceptedPredictionTokens":0,"rejectedPredictionTokens":0,"cachedPromptTokens":0}},"experimental_providerMetadata":{"openai":{"reasoningTokens":0,"acceptedPredictionTokens":0,"rejectedPredictionTokens":0,"cachedPromptTokens":0}},"isContinued":false}],"experimental_providerMetadata":{"openai":{"reasoningTokens":0,"acceptedPredictionTokens":0,"rejectedPredictionTokens":0,"cachedPromptTokens":0}},"providerMetadata":{"openai":{"reasoningTokens":0,"acceptedPredictionTokens":0,"rejectedPredictionTokens":0,"cachedPromptTokens":0}},"sources":[]}' + recorded_at: Thu, 24 Jul 2025 12:43:22 GMT recorded_with: VCR 6.3.1 diff --git a/spec/lib/ai/agent_spec.rb b/spec/lib/ai/agent_spec.rb index 8a188ab..95e907f 100644 --- a/spec/lib/ai/agent_spec.rb +++ b/spec/lib/ai/agent_spec.rb @@ -52,7 +52,7 @@ it 'passes telemetry settings to client' do telemetry_settings = Ai::TelemetrySettings.new( - is_enabled: true, + enabled: true, record_inputs: true, record_outputs: true, function_id: 'test-function', @@ -160,7 +160,7 @@ it 'passes telemetry settings for object generation' do telemetry_settings = Ai::TelemetrySettings.new( - is_enabled: true, + enabled: true, record_inputs: false, record_outputs: true, function_id: 'object-generation', diff --git a/spec/lib/ai/mastra_client_spec.rb b/spec/lib/ai/mastra_client_spec.rb index b621653..0bde16a 100644 --- a/spec/lib/ai/mastra_client_spec.rb +++ b/spec/lib/ai/mastra_client_spec.rb @@ -33,7 +33,7 @@ it 'generates text using the Mastra API' do telemetry_settings = Ai::TelemetrySettings.new( - is_enabled: true, + enabled: true, record_inputs: true, record_outputs: true, function_id: 'mastra-text-generation', @@ -55,7 +55,7 @@ it 'generates structured object using the Mastra API' do telemetry_settings = Ai::TelemetrySettings.new( - is_enabled: true, + enabled: true, record_inputs: false, record_outputs: true, function_id: 'mastra-object-generation', @@ -79,7 +79,18 @@ expect(result.dig('object', 'age')).to eq(0) end end + end + + describe '#map_ruby_key_to_api' do + let(:client) { described_class.new('https://app.example.com') } + it 'maps enabled to is_enabled for API compatibility' do + expect(client.send(:map_ruby_key_to_api, 'enabled')).to eq('is_enabled') + end + it 'returns unmapped keys as-is' do + expect(client.send(:map_ruby_key_to_api, 'record_inputs')).to eq('record_inputs') + expect(client.send(:map_ruby_key_to_api, 'function_id')).to eq('function_id') + end end end diff --git a/spec/lib/ai/telemetry_settings_spec.rb b/spec/lib/ai/telemetry_settings_spec.rb index 770c68e..2ac9377 100644 --- a/spec/lib/ai/telemetry_settings_spec.rb +++ b/spec/lib/ai/telemetry_settings_spec.rb @@ -2,74 +2,76 @@ # frozen_string_literal: true RSpec.describe Ai::TelemetrySettings do - describe 'initialization with defaults' do - subject { described_class.new } + describe 'initialization' do + context 'with defaults' do + subject { described_class.new } - it 'sets correct default values' do - expect(subject.is_enabled).to be true - expect(subject.record_inputs).to be false - expect(subject.record_outputs).to be false - expect(subject.function_id).to be_nil - expect(subject.metadata).to eq({}) - expect(subject.tracer).to be_nil + it 'sets correct default values' do + expect(subject.enabled).to be true + expect(subject.record_inputs).to be false + expect(subject.record_outputs).to be false + expect(subject.function_id).to be_nil + expect(subject.metadata).to eq({}) + expect(subject.tracer).to be_nil + end end - end - describe 'initialization with custom values' do - subject do - described_class.new( - is_enabled: true, - record_inputs: false, - record_outputs: false, - function_id: 'my-custom-function', - metadata: { 'agent.name' => 'test-agent', 'service.version' => '1.0.0' }, - tracer: 'custom-tracer' - ) - end + context 'with custom values' do + subject do + described_class.new( + enabled: true, + record_inputs: false, + record_outputs: false, + function_id: 'my-custom-function', + metadata: { 'agent.name' => 'test-agent', 'service.version' => '1.0.0' }, + tracer: 'custom-tracer' + ) + end - it 'sets custom values correctly' do - expect(subject.is_enabled).to be true - expect(subject.record_inputs).to be false - expect(subject.record_outputs).to be false - expect(subject.function_id).to eq('my-custom-function') - expect(subject.metadata).to eq({ 'agent.name' => 'test-agent', 'service.version' => '1.0.0' }) - expect(subject.tracer).to eq('custom-tracer') + it 'sets custom values correctly' do + expect(subject.enabled).to be true + expect(subject.record_inputs).to be false + expect(subject.record_outputs).to be false + expect(subject.function_id).to eq('my-custom-function') + expect(subject.metadata).to eq({ 'agent.name' => 'test-agent', 'service.version' => '1.0.0' }) + expect(subject.tracer).to eq('custom-tracer') + end end end describe 'privacy and security configurations' do it 'allows disabling input recording for sensitive data' do settings = described_class.new( - is_enabled: true, + enabled: true, record_inputs: false, record_outputs: true ) - expect(settings.is_enabled).to be true + expect(settings.enabled).to be true expect(settings.record_inputs).to be false expect(settings.record_outputs).to be true end it 'allows disabling output recording for sensitive data' do settings = described_class.new( - is_enabled: true, + enabled: true, record_inputs: true, record_outputs: false ) - expect(settings.is_enabled).to be true + expect(settings.enabled).to be true expect(settings.record_inputs).to be true expect(settings.record_outputs).to be false end it 'allows disabling both inputs and outputs' do settings = described_class.new( - is_enabled: true, + enabled: true, record_inputs: false, record_outputs: false ) - expect(settings.is_enabled).to be true + expect(settings.enabled).to be true expect(settings.record_inputs).to be false expect(settings.record_outputs).to be false end @@ -78,7 +80,7 @@ describe 'agent identification metadata' do it 'supports agent identification in metadata' do settings = described_class.new( - is_enabled: true, + enabled: true, function_id: 'agent-marvin-conversation', metadata: { 'agent.name' => 'marvin',