@azure/ai-projects 2.8.0 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/README.md +73 -5
  2. package/dist/browser/aiProjectClient.d.ts.map +1 -1
  3. package/dist/browser/aiProjectClient.js +1 -1
  4. package/dist/browser/aiProjectClient.js.map +1 -1
  5. package/dist/browser/classic/beta/agents/index.d.ts +2 -1
  6. package/dist/browser/classic/beta/agents/index.d.ts.map +1 -1
  7. package/dist/browser/classic/beta/agents/index.js +3 -2
  8. package/dist/browser/classic/beta/agents/index.js.map +1 -1
  9. package/dist/browser/classic/beta/index.d.ts +2 -1
  10. package/dist/browser/classic/beta/index.d.ts.map +1 -1
  11. package/dist/browser/classic/beta/index.js +3 -3
  12. package/dist/browser/classic/beta/index.js.map +1 -1
  13. package/dist/browser/classic/beta/voiceAgents/conversations/index.d.ts +2 -1
  14. package/dist/browser/classic/beta/voiceAgents/conversations/index.d.ts.map +1 -1
  15. package/dist/browser/classic/beta/voiceAgents/conversations/index.js +5 -4
  16. package/dist/browser/classic/beta/voiceAgents/conversations/index.js.map +1 -1
  17. package/dist/browser/classic/beta/voiceAgents/index.d.ts +3 -2
  18. package/dist/browser/classic/beta/voiceAgents/index.d.ts.map +1 -1
  19. package/dist/browser/classic/beta/voiceAgents/index.js +5 -5
  20. package/dist/browser/classic/beta/voiceAgents/index.js.map +1 -1
  21. package/dist/browser/classic/beta/voiceAgents/telephony/index.d.ts +2 -1
  22. package/dist/browser/classic/beta/voiceAgents/telephony/index.d.ts.map +1 -1
  23. package/dist/browser/classic/beta/voiceAgents/telephony/index.js +16 -11
  24. package/dist/browser/classic/beta/voiceAgents/telephony/index.js.map +1 -1
  25. package/dist/browser/constants.d.ts +1 -1
  26. package/dist/browser/constants.js +1 -1
  27. package/dist/browser/constants.js.map +1 -1
  28. package/dist/browser/index.d.ts +1 -1
  29. package/dist/browser/index.d.ts.map +1 -1
  30. package/dist/browser/index.js.map +1 -1
  31. package/dist/browser/models/index.d.ts +1 -1
  32. package/dist/browser/models/index.d.ts.map +1 -1
  33. package/dist/browser/models/index.js.map +1 -1
  34. package/dist/browser/models/models.d.ts +203 -158
  35. package/dist/browser/models/models.d.ts.map +1 -1
  36. package/dist/browser/models/models.js +129 -120
  37. package/dist/browser/models/models.js.map +1 -1
  38. package/dist/browser/package.json +1 -1
  39. package/dist/browser/realtime/voiceAgentRealtimeClient.d.ts +3 -0
  40. package/dist/browser/realtime/voiceAgentRealtimeClient.d.ts.map +1 -1
  41. package/dist/browser/realtime/voiceAgentRealtimeClient.js +39 -8
  42. package/dist/browser/realtime/voiceAgentRealtimeClient.js.map +1 -1
  43. package/dist/browser/tracing/configuration.d.ts +3 -5
  44. package/dist/browser/tracing/configuration.d.ts.map +1 -1
  45. package/dist/browser/tracing/configuration.js +8 -6
  46. package/dist/browser/tracing/configuration.js.map +1 -1
  47. package/dist/browser/tracing/tracingClient.d.ts +1 -1
  48. package/dist/browser/tracing/tracingClient.d.ts.map +1 -1
  49. package/dist/browser/tracing/tracingClient.js +2 -2
  50. package/dist/browser/tracing/tracingClient.js.map +1 -1
  51. package/dist/browser/tracing/voiceAgentTracing.d.ts +63 -0
  52. package/dist/browser/tracing/voiceAgentTracing.d.ts.map +1 -0
  53. package/dist/browser/tracing/voiceAgentTracing.js +740 -0
  54. package/dist/browser/tracing/voiceAgentTracing.js.map +1 -0
  55. package/dist/commonjs/aiProjectClient.d.ts.map +1 -1
  56. package/dist/commonjs/aiProjectClient.js +2 -1
  57. package/dist/commonjs/aiProjectClient.js.map +2 -2
  58. package/dist/commonjs/classic/beta/agents/index.d.ts +2 -1
  59. package/dist/commonjs/classic/beta/agents/index.d.ts.map +1 -1
  60. package/dist/commonjs/classic/beta/agents/index.js +9 -2
  61. package/dist/commonjs/classic/beta/agents/index.js.map +2 -2
  62. package/dist/commonjs/classic/beta/index.d.ts +2 -1
  63. package/dist/commonjs/classic/beta/index.d.ts.map +1 -1
  64. package/dist/commonjs/classic/beta/index.js +9 -3
  65. package/dist/commonjs/classic/beta/index.js.map +2 -2
  66. package/dist/commonjs/classic/beta/voiceAgents/conversations/index.d.ts +2 -1
  67. package/dist/commonjs/classic/beta/voiceAgents/conversations/index.d.ts.map +1 -1
  68. package/dist/commonjs/classic/beta/voiceAgents/conversations/index.js +10 -4
  69. package/dist/commonjs/classic/beta/voiceAgents/conversations/index.js.map +2 -2
  70. package/dist/commonjs/classic/beta/voiceAgents/index.d.ts +3 -2
  71. package/dist/commonjs/classic/beta/voiceAgents/index.d.ts.map +1 -1
  72. package/dist/commonjs/classic/beta/voiceAgents/index.js +4 -4
  73. package/dist/commonjs/classic/beta/voiceAgents/index.js.map +2 -2
  74. package/dist/commonjs/classic/beta/voiceAgents/telephony/index.d.ts +2 -1
  75. package/dist/commonjs/classic/beta/voiceAgents/telephony/index.d.ts.map +1 -1
  76. package/dist/commonjs/classic/beta/voiceAgents/telephony/index.js +33 -11
  77. package/dist/commonjs/classic/beta/voiceAgents/telephony/index.js.map +2 -2
  78. package/dist/commonjs/constants.d.ts +1 -1
  79. package/dist/commonjs/constants.js +1 -1
  80. package/dist/commonjs/constants.js.map +1 -1
  81. package/dist/commonjs/index.d.ts +1 -1
  82. package/dist/commonjs/index.d.ts.map +1 -1
  83. package/dist/commonjs/index.js.map +1 -1
  84. package/dist/commonjs/models/index.d.ts +1 -1
  85. package/dist/commonjs/models/index.d.ts.map +1 -1
  86. package/dist/commonjs/models/index.js.map +1 -1
  87. package/dist/commonjs/models/models.d.ts +203 -158
  88. package/dist/commonjs/models/models.d.ts.map +1 -1
  89. package/dist/commonjs/models/models.js +145 -106
  90. package/dist/commonjs/models/models.js.map +2 -2
  91. package/dist/commonjs/package.json +1 -1
  92. package/dist/commonjs/realtime/voiceAgentRealtimeClient.d.ts +3 -0
  93. package/dist/commonjs/realtime/voiceAgentRealtimeClient.d.ts.map +1 -1
  94. package/dist/commonjs/realtime/voiceAgentRealtimeClient.js +45 -17
  95. package/dist/commonjs/realtime/voiceAgentRealtimeClient.js.map +2 -2
  96. package/dist/commonjs/tracing/configuration.d.ts +3 -5
  97. package/dist/commonjs/tracing/configuration.d.ts.map +1 -1
  98. package/dist/commonjs/tracing/configuration.js +5 -1
  99. package/dist/commonjs/tracing/configuration.js.map +2 -2
  100. package/dist/commonjs/tracing/tracingClient.d.ts +1 -1
  101. package/dist/commonjs/tracing/tracingClient.d.ts.map +1 -1
  102. package/dist/commonjs/tracing/tracingClient.js +2 -2
  103. package/dist/commonjs/tracing/tracingClient.js.map +2 -2
  104. package/dist/commonjs/tracing/voiceAgentTracing.d.ts +63 -0
  105. package/dist/commonjs/tracing/voiceAgentTracing.d.ts.map +1 -0
  106. package/dist/commonjs/tracing/voiceAgentTracing.js +705 -0
  107. package/dist/commonjs/tracing/voiceAgentTracing.js.map +7 -0
  108. package/dist/commonjs/tsdoc-metadata.json +1 -1
  109. package/dist/esm/aiProjectClient.d.ts.map +1 -1
  110. package/dist/esm/aiProjectClient.js +1 -1
  111. package/dist/esm/aiProjectClient.js.map +1 -1
  112. package/dist/esm/classic/beta/agents/index.d.ts +2 -1
  113. package/dist/esm/classic/beta/agents/index.d.ts.map +1 -1
  114. package/dist/esm/classic/beta/agents/index.js +3 -2
  115. package/dist/esm/classic/beta/agents/index.js.map +1 -1
  116. package/dist/esm/classic/beta/index.d.ts +2 -1
  117. package/dist/esm/classic/beta/index.d.ts.map +1 -1
  118. package/dist/esm/classic/beta/index.js +3 -3
  119. package/dist/esm/classic/beta/index.js.map +1 -1
  120. package/dist/esm/classic/beta/voiceAgents/conversations/index.d.ts +2 -1
  121. package/dist/esm/classic/beta/voiceAgents/conversations/index.d.ts.map +1 -1
  122. package/dist/esm/classic/beta/voiceAgents/conversations/index.js +5 -4
  123. package/dist/esm/classic/beta/voiceAgents/conversations/index.js.map +1 -1
  124. package/dist/esm/classic/beta/voiceAgents/index.d.ts +3 -2
  125. package/dist/esm/classic/beta/voiceAgents/index.d.ts.map +1 -1
  126. package/dist/esm/classic/beta/voiceAgents/index.js +5 -5
  127. package/dist/esm/classic/beta/voiceAgents/index.js.map +1 -1
  128. package/dist/esm/classic/beta/voiceAgents/telephony/index.d.ts +2 -1
  129. package/dist/esm/classic/beta/voiceAgents/telephony/index.d.ts.map +1 -1
  130. package/dist/esm/classic/beta/voiceAgents/telephony/index.js +16 -11
  131. package/dist/esm/classic/beta/voiceAgents/telephony/index.js.map +1 -1
  132. package/dist/esm/constants.d.ts +1 -1
  133. package/dist/esm/constants.js +1 -1
  134. package/dist/esm/constants.js.map +1 -1
  135. package/dist/esm/index.d.ts +1 -1
  136. package/dist/esm/index.d.ts.map +1 -1
  137. package/dist/esm/index.js.map +1 -1
  138. package/dist/esm/models/index.d.ts +1 -1
  139. package/dist/esm/models/index.d.ts.map +1 -1
  140. package/dist/esm/models/index.js.map +1 -1
  141. package/dist/esm/models/models.d.ts +203 -158
  142. package/dist/esm/models/models.d.ts.map +1 -1
  143. package/dist/esm/models/models.js +129 -120
  144. package/dist/esm/models/models.js.map +1 -1
  145. package/dist/esm/package.json +1 -1
  146. package/dist/esm/realtime/voiceAgentRealtimeClient.d.ts +3 -0
  147. package/dist/esm/realtime/voiceAgentRealtimeClient.d.ts.map +1 -1
  148. package/dist/esm/realtime/voiceAgentRealtimeClient.js +39 -8
  149. package/dist/esm/realtime/voiceAgentRealtimeClient.js.map +1 -1
  150. package/dist/esm/tracing/configuration.d.ts +3 -5
  151. package/dist/esm/tracing/configuration.d.ts.map +1 -1
  152. package/dist/esm/tracing/configuration.js +8 -6
  153. package/dist/esm/tracing/configuration.js.map +1 -1
  154. package/dist/esm/tracing/tracingClient.d.ts +1 -1
  155. package/dist/esm/tracing/tracingClient.d.ts.map +1 -1
  156. package/dist/esm/tracing/tracingClient.js +2 -2
  157. package/dist/esm/tracing/tracingClient.js.map +1 -1
  158. package/dist/esm/tracing/voiceAgentTracing.d.ts +63 -0
  159. package/dist/esm/tracing/voiceAgentTracing.d.ts.map +1 -0
  160. package/dist/esm/tracing/voiceAgentTracing.js +740 -0
  161. package/dist/esm/tracing/voiceAgentTracing.js.map +1 -0
  162. package/dist/react-native/aiProjectClient.d.ts.map +1 -1
  163. package/dist/react-native/aiProjectClient.js +1 -1
  164. package/dist/react-native/aiProjectClient.js.map +1 -1
  165. package/dist/react-native/classic/beta/agents/index.d.ts +2 -1
  166. package/dist/react-native/classic/beta/agents/index.d.ts.map +1 -1
  167. package/dist/react-native/classic/beta/agents/index.js +3 -2
  168. package/dist/react-native/classic/beta/agents/index.js.map +1 -1
  169. package/dist/react-native/classic/beta/index.d.ts +2 -1
  170. package/dist/react-native/classic/beta/index.d.ts.map +1 -1
  171. package/dist/react-native/classic/beta/index.js +3 -3
  172. package/dist/react-native/classic/beta/index.js.map +1 -1
  173. package/dist/react-native/classic/beta/voiceAgents/conversations/index.d.ts +2 -1
  174. package/dist/react-native/classic/beta/voiceAgents/conversations/index.d.ts.map +1 -1
  175. package/dist/react-native/classic/beta/voiceAgents/conversations/index.js +5 -4
  176. package/dist/react-native/classic/beta/voiceAgents/conversations/index.js.map +1 -1
  177. package/dist/react-native/classic/beta/voiceAgents/index.d.ts +3 -2
  178. package/dist/react-native/classic/beta/voiceAgents/index.d.ts.map +1 -1
  179. package/dist/react-native/classic/beta/voiceAgents/index.js +5 -5
  180. package/dist/react-native/classic/beta/voiceAgents/index.js.map +1 -1
  181. package/dist/react-native/classic/beta/voiceAgents/telephony/index.d.ts +2 -1
  182. package/dist/react-native/classic/beta/voiceAgents/telephony/index.d.ts.map +1 -1
  183. package/dist/react-native/classic/beta/voiceAgents/telephony/index.js +16 -11
  184. package/dist/react-native/classic/beta/voiceAgents/telephony/index.js.map +1 -1
  185. package/dist/react-native/constants.d.ts +1 -1
  186. package/dist/react-native/constants.js +1 -1
  187. package/dist/react-native/constants.js.map +1 -1
  188. package/dist/react-native/index.d.ts +1 -1
  189. package/dist/react-native/index.d.ts.map +1 -1
  190. package/dist/react-native/index.js.map +1 -1
  191. package/dist/react-native/models/index.d.ts +1 -1
  192. package/dist/react-native/models/index.d.ts.map +1 -1
  193. package/dist/react-native/models/index.js.map +1 -1
  194. package/dist/react-native/models/models.d.ts +203 -158
  195. package/dist/react-native/models/models.d.ts.map +1 -1
  196. package/dist/react-native/models/models.js +129 -120
  197. package/dist/react-native/models/models.js.map +1 -1
  198. package/dist/react-native/package.json +1 -1
  199. package/dist/react-native/realtime/voiceAgentRealtimeClient.d.ts +3 -0
  200. package/dist/react-native/realtime/voiceAgentRealtimeClient.d.ts.map +1 -1
  201. package/dist/react-native/realtime/voiceAgentRealtimeClient.js +39 -8
  202. package/dist/react-native/realtime/voiceAgentRealtimeClient.js.map +1 -1
  203. package/dist/react-native/tracing/configuration.d.ts +3 -5
  204. package/dist/react-native/tracing/configuration.d.ts.map +1 -1
  205. package/dist/react-native/tracing/configuration.js +8 -6
  206. package/dist/react-native/tracing/configuration.js.map +1 -1
  207. package/dist/react-native/tracing/tracingClient.d.ts +1 -1
  208. package/dist/react-native/tracing/tracingClient.d.ts.map +1 -1
  209. package/dist/react-native/tracing/tracingClient.js +2 -2
  210. package/dist/react-native/tracing/tracingClient.js.map +1 -1
  211. package/dist/react-native/tracing/voiceAgentTracing.d.ts +63 -0
  212. package/dist/react-native/tracing/voiceAgentTracing.d.ts.map +1 -0
  213. package/dist/react-native/tracing/voiceAgentTracing.js +740 -0
  214. package/dist/react-native/tracing/voiceAgentTracing.js.map +1 -0
  215. package/package.json +6 -6
@@ -0,0 +1,740 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+ import { SpanStatusCode } from "@opentelemetry/api";
4
+ import { isRestError } from "@azure/core-rest-pipeline";
5
+ import { logger } from "../logger.js";
6
+ import { setServerAttributes, parseEndpoint } from "./attributes.js";
7
+ import { startSpan, runInSpanContext } from "./tracingClient.js";
8
+ import { recordOperationDuration, recordTokenUsage } from "./metrics.js";
9
+ import { AGENTS_PROVIDER, AZ_NAMESPACE, AZ_NAMESPACE_VALUE, GEN_AI_PROVIDER_NAME, GEN_AI_OPERATION_NAME, GEN_AI_INPUT_MESSAGES, GEN_AI_OUTPUT_MESSAGES, GEN_AI_REQUEST_MODEL, GEN_AI_RESPONSE_ID, GEN_AI_CONVERSATION_ID, GEN_AI_USAGE_INPUT_TOKENS, GEN_AI_USAGE_OUTPUT_TOKENS, GEN_AI_TOOL_CALL_ID, ERROR_TYPE, } from "./constants.js";
10
+ const prefix = "az.ai.voice_agents.";
11
+ const maxResponses = 64;
12
+ const maxInputItems = 64;
13
+ const maxParts = 32;
14
+ const maxContentLength = 4096;
15
+ const safeErrorTypes = new Set([
16
+ "Error",
17
+ "TypeError",
18
+ "AbortError",
19
+ "RestError",
20
+ "VoiceAgentAuthenticationError",
21
+ "VoiceAgentConnectionError",
22
+ "VoiceAgentProtocolError",
23
+ "invalid_request_error",
24
+ "server_error",
25
+ "authentication_error",
26
+ "rate_limit_error",
27
+ ]);
28
+ const safeErrorCodes = new Set([
29
+ "agent_disabled",
30
+ "invalid_request",
31
+ "invalid_request_error",
32
+ "invalid_event",
33
+ "invalid_state",
34
+ "rate_limit_exceeded",
35
+ "insufficient_quota",
36
+ "session_expired",
37
+ "authentication_error",
38
+ "permission_denied",
39
+ "server_error",
40
+ ]);
41
+ function serverErrorAttributes(type, code) {
42
+ const attributes = {
43
+ [ERROR_TYPE]: typeof type === "string" && safeErrorTypes.has(type) ? type : "Error",
44
+ };
45
+ if (typeof code === "string" && safeErrorCodes.has(code)) {
46
+ attributes[`${prefix}error.code`] = code;
47
+ }
48
+ return attributes;
49
+ }
50
+ // Exporter/provider failures must not change a service operation's result.
51
+ function recordTelemetry(action) {
52
+ try {
53
+ action();
54
+ }
55
+ catch {
56
+ logger.warning("Unable to record Voice Agents telemetry.");
57
+ }
58
+ }
59
+ function commonAttributes(span, endpoint) {
60
+ span.setAttribute(GEN_AI_PROVIDER_NAME, AGENTS_PROVIDER);
61
+ span.setAttribute(AZ_NAMESPACE, AZ_NAMESPACE_VALUE);
62
+ setServerAttributes(span, endpoint);
63
+ }
64
+ function fail(span, error) {
65
+ const type = isRestError(error)
66
+ ? "RestError"
67
+ : error instanceof Error && safeErrorTypes.has(error.name)
68
+ ? error.name
69
+ : "Error";
70
+ span.setAttribute(ERROR_TYPE, type);
71
+ if (isRestError(error) && error.statusCode !== undefined) {
72
+ span.setAttribute("http.response.status_code", error.statusCode);
73
+ }
74
+ span.setStatus({ code: SpanStatusCode.ERROR });
75
+ }
76
+ /** @internal */
77
+ export async function traceVoiceAgentOperation(operationName, endpoint, config, operation, resultAttributes) {
78
+ if (!config?.enabled) {
79
+ return operation();
80
+ }
81
+ let recording;
82
+ recordTelemetry(() => {
83
+ recording = startSpan(operationName);
84
+ commonAttributes(recording.span, endpoint);
85
+ recording.span.setAttribute(operationName === "create_agent" ? GEN_AI_OPERATION_NAME : `${prefix}operation`, operationName.slice(operationName.lastIndexOf(".") + 1));
86
+ });
87
+ if (!recording)
88
+ return operation();
89
+ const { span, ctx } = recording;
90
+ try {
91
+ const result = await runInSpanContext(ctx, operation);
92
+ recordTelemetry(() => {
93
+ if (resultAttributes)
94
+ span.setAttributes(resultAttributes(result));
95
+ });
96
+ return result;
97
+ }
98
+ catch (error) {
99
+ recordTelemetry(() => {
100
+ if (!(error instanceof Error && error.name === "AbortError"))
101
+ fail(span, error);
102
+ else
103
+ span.setAttribute(`${prefix}cancelled`, true);
104
+ });
105
+ throw error;
106
+ }
107
+ finally {
108
+ recordTelemetry(() => span.end());
109
+ }
110
+ }
111
+ function asRecord(value) {
112
+ return value !== null && typeof value === "object" && !Array.isArray(value)
113
+ ? value
114
+ : undefined;
115
+ }
116
+ function limitedContent(value) {
117
+ return typeof value === "string" ? value.slice(0, maxContentLength) : undefined;
118
+ }
119
+ function messagePart(value, contentRecording) {
120
+ const part = asRecord(value);
121
+ const type = part?.type;
122
+ if (!part ||
123
+ typeof type !== "string" ||
124
+ !["input_text", "output_text", "text", "input_audio", "output_audio", "audio"].includes(type)) {
125
+ return undefined;
126
+ }
127
+ const audio = type.includes("audio");
128
+ const summary = { type: audio ? "audio" : "text" };
129
+ if (contentRecording) {
130
+ const content = limitedContent(audio ? part.transcript : part.text);
131
+ if (content !== undefined)
132
+ summary.content = content;
133
+ }
134
+ return summary;
135
+ }
136
+ function messages(item, contentRecording) {
137
+ const record = asRecord(item);
138
+ if (!record)
139
+ return undefined;
140
+ if (record.type === "function_call" ||
141
+ record.type === "function_call_output" ||
142
+ record.type === "mcp_call") {
143
+ const part = {
144
+ type: record.type === "function_call_output" ? "tool_call_response" : "tool_call",
145
+ };
146
+ if (typeof record.call_id === "string")
147
+ part.id = record.call_id;
148
+ if (contentRecording) {
149
+ if (typeof record.name === "string")
150
+ part.name = limitedContent(record.name);
151
+ if (typeof record.arguments === "string")
152
+ part.arguments = limitedContent(record.arguments);
153
+ if (typeof record.output === "string")
154
+ part.result = limitedContent(record.output);
155
+ }
156
+ return { role: record.type === "function_call_output" ? "tool" : "assistant", parts: [part] };
157
+ }
158
+ if (record.type !== "message")
159
+ return undefined;
160
+ const parts = [];
161
+ if (Array.isArray(record.content)) {
162
+ for (const value of record.content.slice(0, maxParts)) {
163
+ const summary = messagePart(value, contentRecording);
164
+ if (summary)
165
+ parts.push(summary);
166
+ }
167
+ }
168
+ const role = record.role === "system" || record.role === "user" ? record.role : "assistant";
169
+ return parts.length ? { role, parts } : undefined;
170
+ }
171
+ /** Tracks SDK-observed realtime work, independently of the user's event iterator. @internal */
172
+ export class VoiceAgentRealtimeTracing {
173
+ endpoint;
174
+ config;
175
+ responses = new Map();
176
+ session;
177
+ connectSpan;
178
+ ended = false;
179
+ connected = false;
180
+ model;
181
+ inputAudioBytes = 0;
182
+ inputSequence = 0;
183
+ nextInputSequence = 0;
184
+ inputAudioBuffer;
185
+ inputAudioItems = new Map();
186
+ pendingAudioCommits = { items: [], skipped: 0 };
187
+ pendingAudioMessages = { items: [], skipped: 0 };
188
+ pendingAudioClears = 0;
189
+ untrackedInputItems = 0;
190
+ finishedResponses = 0;
191
+ untrackedResponses = 0;
192
+ constructor(endpoint, config, transport = "websocket") {
193
+ this.endpoint = endpoint;
194
+ this.config = config;
195
+ recordTelemetry(() => {
196
+ this.session = startSpan("voice_agents.realtime.session");
197
+ commonAttributes(this.session.span, endpoint);
198
+ this.session.span.setAttribute(`${prefix}connection.transport`, transport);
199
+ this.connectSpan = startSpan("voice_agents.realtime.connect", this.session.ctx).span;
200
+ commonAttributes(this.connectSpan, endpoint);
201
+ this.connectSpan.setAttribute(`${prefix}connection.transport`, transport);
202
+ });
203
+ }
204
+ opened() {
205
+ recordTelemetry(() => {
206
+ if (this.ended)
207
+ return;
208
+ this.connected = true;
209
+ this.connectSpan?.end();
210
+ this.connectSpan = undefined;
211
+ });
212
+ }
213
+ reserveSend(event) {
214
+ let reservation;
215
+ recordTelemetry(() => {
216
+ if (this.ended || !this.session || event.type !== "conversation.item.create")
217
+ return;
218
+ const summary = messages(event.item, this.config.contentRecording);
219
+ const item = asRecord(event.item);
220
+ if (!summary)
221
+ return;
222
+ reservation = { sequence: ++this.nextInputSequence };
223
+ if (summary.role !== "user" ||
224
+ !summary.parts.some((part) => part.type === "audio") ||
225
+ typeof item?.id === "string" ||
226
+ this.pendingAudioMessages.items.length >= maxInputItems ||
227
+ this.pendingAudioMessages.skipped > 0) {
228
+ return;
229
+ }
230
+ reservation.audio = { sequence: reservation.sequence, pendingSend: true };
231
+ this.enqueueInputAudio(this.pendingAudioMessages, reservation.audio);
232
+ });
233
+ return reservation;
234
+ }
235
+ sent(event, reservation) {
236
+ recordTelemetry(() => {
237
+ if (this.ended || !this.session)
238
+ return;
239
+ if (event.type === "input_audio_buffer.append") {
240
+ // Count decoded bytes without retaining or decoding the media.
241
+ this.inputAudioBytes += Math.max(0, Math.floor((event.audio.length * 3) / 4) -
242
+ (event.audio.endsWith("==") ? 2 : event.audio.endsWith("=") ? 1 : 0));
243
+ if (!this.inputAudioBuffer) {
244
+ this.inputAudioBuffer = this.startInputAudio();
245
+ }
246
+ else if (this.inputAudioBuffer.sequence !== this.inputSequence) {
247
+ this.inputSequence = ++this.nextInputSequence;
248
+ this.inputAudioBuffer.sequence = this.inputSequence;
249
+ this.setInputAudioSummary();
250
+ }
251
+ }
252
+ else if (event.type === "input_audio_buffer.commit") {
253
+ if (this.inputAudioBuffer) {
254
+ this.enqueueInputAudio(this.pendingAudioCommits, this.inputAudioBuffer);
255
+ this.inputAudioBuffer = undefined;
256
+ }
257
+ }
258
+ else if (event.type === "input_audio_buffer.clear") {
259
+ this.inputAudioBuffer = undefined;
260
+ this.pendingAudioClears++;
261
+ }
262
+ else if (event.type === "conversation.item.create") {
263
+ const summary = messages(event.item, this.config.contentRecording);
264
+ const item = asRecord(event.item);
265
+ if (summary) {
266
+ const sequence = reservation?.sequence ?? ++this.nextInputSequence;
267
+ if (summary.role === "user" && summary.parts.some((part) => part.type === "audio")) {
268
+ const input = reservation?.audio ?? { sequence };
269
+ input.pendingSend = undefined;
270
+ if (!reservation?.audio) {
271
+ if (typeof item?.id === "string") {
272
+ this.rememberInputAudio(item.id, input);
273
+ }
274
+ else {
275
+ this.enqueueInputAudio(this.pendingAudioMessages, input);
276
+ }
277
+ }
278
+ }
279
+ if (sequence >= this.inputSequence) {
280
+ this.inputSequence = sequence;
281
+ this.session.span.setAttribute(GEN_AI_INPUT_MESSAGES, JSON.stringify([summary]));
282
+ if (reservation?.audio?.pendingTranscript !== undefined) {
283
+ this.setInputAudioSummary(reservation.audio.pendingTranscript);
284
+ }
285
+ }
286
+ }
287
+ if (item?.type === "function_call_output" && typeof item.call_id === "string") {
288
+ this.session.span.addEvent("voice_agents.tool.output_sent", {
289
+ [GEN_AI_TOOL_CALL_ID]: item.call_id,
290
+ });
291
+ }
292
+ }
293
+ else if (event.type === "response.cancel") {
294
+ this.session.span.addEvent("voice_agents.response.cancel_requested");
295
+ }
296
+ });
297
+ }
298
+ sendFailed(error, reservation) {
299
+ recordTelemetry(() => {
300
+ if (reservation?.audio)
301
+ this.rollbackPendingAudioMessage(reservation.audio);
302
+ if (!this.ended) {
303
+ this.session?.span.addEvent("voice_agents.send.failed", {
304
+ [ERROR_TYPE]: error instanceof Error && safeErrorTypes.has(error.name) ? error.name : "Error",
305
+ });
306
+ }
307
+ });
308
+ }
309
+ received(event) {
310
+ recordTelemetry(() => {
311
+ if (this.ended || !this.session || event.type === "unknown")
312
+ return;
313
+ switch (event.type) {
314
+ case "session.created":
315
+ case "session.updated":
316
+ this.model = event.session.model;
317
+ this.session.span.setAttribute(`${prefix}session.id`, event.session.id);
318
+ if (event.type === "session.created" && event.conversation_id) {
319
+ this.session.span.setAttribute(GEN_AI_CONVERSATION_ID, event.conversation_id);
320
+ }
321
+ if (this.model)
322
+ this.session.span.setAttribute(GEN_AI_REQUEST_MODEL, this.model);
323
+ break;
324
+ case "input_audio_buffer.speech_started":
325
+ if (!this.inputAudioItems.has(event.item_id)) {
326
+ const buffer = this.inputAudioBuffer;
327
+ const input = buffer && buffer.itemId === undefined ? buffer : this.startInputAudio();
328
+ this.rememberInputAudio(event.item_id, input);
329
+ this.inputAudioBuffer = input;
330
+ }
331
+ break;
332
+ case "input_audio_buffer.committed": {
333
+ const pending = this.pendingAudioCommits.items.length > 0 || this.pendingAudioCommits.skipped > 0;
334
+ let input = this.matchInputAudio(this.pendingAudioCommits, event.item_id);
335
+ if (!input) {
336
+ if (pending)
337
+ break;
338
+ input = this.inputAudioBuffer ?? this.startInputAudio();
339
+ this.rememberInputAudio(event.item_id, input);
340
+ }
341
+ if (this.inputAudioBuffer === input)
342
+ this.inputAudioBuffer = undefined;
343
+ break;
344
+ }
345
+ case "input_audio_buffer.cleared":
346
+ if (this.pendingAudioClears > 0)
347
+ this.pendingAudioClears--;
348
+ else
349
+ this.inputAudioBuffer = undefined;
350
+ break;
351
+ case "conversation.item.added":
352
+ case "conversation.item.created": {
353
+ const item = asRecord(event.item);
354
+ const summary = messages(event.item, false);
355
+ if (typeof item?.id === "string" &&
356
+ summary?.role === "user" &&
357
+ summary.parts.some((part) => part.type === "audio")) {
358
+ this.matchInputAudio(this.pendingAudioMessages, item.id);
359
+ }
360
+ break;
361
+ }
362
+ case "response.created":
363
+ this.beginResponse(event.response);
364
+ break;
365
+ case "response.output_audio.delta": {
366
+ const tracked = this.responses.get(event.response_id);
367
+ if (tracked) {
368
+ tracked.audioBytes += event.delta.byteLength;
369
+ if (event.delta.byteLength > 0) {
370
+ tracked.firstAudioInSeconds ??= (performance.now() - tracked.started) / 1000;
371
+ }
372
+ }
373
+ break;
374
+ }
375
+ case "response.done":
376
+ this.endResponse(event.response);
377
+ break;
378
+ case "response.output_text.done":
379
+ case "response.output_audio_transcript.done": {
380
+ const tracked = this.responses.get(event.response_id);
381
+ if (tracked) {
382
+ const part = {
383
+ type: event.type === "response.output_text.done" ? "text" : "audio",
384
+ };
385
+ if (this.config.contentRecording) {
386
+ part.content = limitedContent(event.type === "response.output_text.done" ? event.text : event.transcript);
387
+ }
388
+ this.recordOutputPart(tracked, event.item_id, event.content_index, part);
389
+ }
390
+ break;
391
+ }
392
+ case "response.output_audio.done": {
393
+ const tracked = this.responses.get(event.response_id);
394
+ if (tracked) {
395
+ this.recordOutputPart(tracked, event.item_id, event.content_index, { type: "audio" });
396
+ }
397
+ break;
398
+ }
399
+ case "response.function_call_arguments.done": {
400
+ const span = this.responses.get(event.response_id)?.span ?? this.session.span;
401
+ const attributes = {
402
+ [GEN_AI_TOOL_CALL_ID]: event.call_id,
403
+ [`${prefix}tool.kind`]: "function",
404
+ };
405
+ if (this.config.contentRecording) {
406
+ attributes["gen_ai.tool.name"] = limitedContent(event.name);
407
+ attributes[`${prefix}tool.arguments`] = limitedContent(event.arguments);
408
+ }
409
+ span.addEvent("voice_agents.tool.requested", attributes);
410
+ const tracked = this.responses.get(event.response_id);
411
+ const summary = messages({
412
+ type: "function_call",
413
+ call_id: event.call_id,
414
+ name: event.name,
415
+ arguments: event.arguments,
416
+ }, this.config.contentRecording);
417
+ if (tracked && summary && tracked.outputMessages.size < maxParts) {
418
+ tracked.outputMessages.set(`tool:${event.call_id}`, summary);
419
+ }
420
+ break;
421
+ }
422
+ case "response.mcp_call.completed":
423
+ case "response.mcp_call.failed":
424
+ this.session.span.addEvent(event.type, { [`${prefix}item.id`]: event.item_id });
425
+ break;
426
+ case "session.subagent.started":
427
+ case "session.subagent.completed":
428
+ case "session.subagent.aborted": {
429
+ const attributes = {
430
+ [`${prefix}subagent.consultation.id`]: event.consultation_id,
431
+ };
432
+ if (event.type === "session.subagent.aborted" &&
433
+ [
434
+ "unknown_target",
435
+ "timeout",
436
+ "cancelled",
437
+ "stopped_by_user",
438
+ "superseded",
439
+ "failed",
440
+ ].includes(event.reason)) {
441
+ attributes[`${prefix}subagent.abort_reason`] = event.reason;
442
+ }
443
+ this.session.span.addEvent(event.type, attributes);
444
+ break;
445
+ }
446
+ case "error":
447
+ this.session.span.addEvent("voice_agents.server_error", serverErrorAttributes(event.error.type, event.error.code));
448
+ break;
449
+ case "warning":
450
+ this.session.span.addEvent("voice_agents.warning");
451
+ break;
452
+ case "rtc.call.error":
453
+ this.session.span.addEvent("rtc.call.error", serverErrorAttributes(event.error.type, event.error.code));
454
+ break;
455
+ case "conversation.item.input_audio_transcription.completed": {
456
+ const input = this.inputAudioItems.get(event.item_id);
457
+ if (input?.pendingSend) {
458
+ input.pendingTranscript = event.transcript;
459
+ }
460
+ else if (input?.sequence === this.inputSequence) {
461
+ this.setInputAudioSummary(event.transcript);
462
+ }
463
+ this.session.span.addEvent("voice_agents.transcription.completed");
464
+ break;
465
+ }
466
+ case "conversation.item.input_audio_transcription.failed": {
467
+ const input = this.inputAudioItems.get(event.item_id);
468
+ if (input?.sequence === this.inputSequence) {
469
+ this.setInputAudioSummary();
470
+ }
471
+ this.session.span.addEvent("voice_agents.transcription.failed");
472
+ break;
473
+ }
474
+ }
475
+ });
476
+ }
477
+ startInputAudio() {
478
+ const input = { sequence: ++this.nextInputSequence };
479
+ this.inputSequence = input.sequence;
480
+ this.setInputAudioSummary();
481
+ return input;
482
+ }
483
+ enqueueInputAudio(queue, input) {
484
+ input.pendingQueue = queue;
485
+ if (queue.items.length >= maxInputItems || queue.skipped > 0) {
486
+ input.pendingSkipped = true;
487
+ queue.skipped++;
488
+ this.untrackedInputItems++;
489
+ }
490
+ else {
491
+ queue.items.push(input);
492
+ }
493
+ }
494
+ rollbackPendingAudioMessage(input) {
495
+ if (input.pendingQueue) {
496
+ const index = input.pendingQueue.items.indexOf(input);
497
+ if (index !== -1) {
498
+ input.pendingQueue.items.splice(index, 1);
499
+ }
500
+ else if (input.pendingSkipped && input.pendingQueue.skipped > 0) {
501
+ input.pendingQueue.skipped--;
502
+ this.untrackedInputItems--;
503
+ }
504
+ input.pendingQueue = undefined;
505
+ input.pendingSkipped = undefined;
506
+ }
507
+ if (input.itemId && this.inputAudioItems.get(input.itemId) === input) {
508
+ this.inputAudioItems.delete(input.itemId);
509
+ }
510
+ input.pendingSend = undefined;
511
+ input.pendingTranscript = undefined;
512
+ }
513
+ matchInputAudio(queue, itemId) {
514
+ const known = this.inputAudioItems.get(itemId);
515
+ if (known) {
516
+ if (known.pendingQueue === queue) {
517
+ const index = queue.items.indexOf(known);
518
+ if (index !== -1)
519
+ queue.items.splice(index, 1);
520
+ else if (known.pendingSkipped && queue.skipped > 0)
521
+ queue.skipped--;
522
+ known.pendingQueue = undefined;
523
+ known.pendingSkipped = undefined;
524
+ }
525
+ return known;
526
+ }
527
+ // Match ordered commit/create acknowledgements, never asynchronous ASR completion order.
528
+ const input = queue.items.shift();
529
+ if (input) {
530
+ input.pendingQueue = undefined;
531
+ this.rememberInputAudio(itemId, input);
532
+ return input;
533
+ }
534
+ if (queue.skipped > 0)
535
+ queue.skipped--;
536
+ return undefined;
537
+ }
538
+ rememberInputAudio(itemId, input) {
539
+ input.itemId = itemId;
540
+ this.inputAudioItems.set(itemId, input);
541
+ if (this.inputAudioItems.size > maxInputItems) {
542
+ for (const [id, tracked] of this.inputAudioItems) {
543
+ if (tracked.sequence !== this.inputSequence || id !== tracked.itemId) {
544
+ this.inputAudioItems.delete(id);
545
+ break;
546
+ }
547
+ }
548
+ }
549
+ }
550
+ setInputAudioSummary(transcript) {
551
+ const parts = [{ type: "audio" }];
552
+ if (this.config.contentRecording && transcript !== undefined) {
553
+ parts[0].content = limitedContent(transcript);
554
+ }
555
+ this.session?.span.setAttribute(GEN_AI_INPUT_MESSAGES, JSON.stringify([{ role: "user", parts }]));
556
+ }
557
+ recordOutputPart(tracked, itemId, contentIndex, part) {
558
+ const key = `${itemId}:${contentIndex}:${part.type}`;
559
+ const existing = tracked.outputParts.get(key);
560
+ if (existing) {
561
+ if (part.content !== undefined)
562
+ existing.part.content = part.content;
563
+ }
564
+ else if (tracked.outputParts.size < maxParts) {
565
+ tracked.outputParts.set(key, { itemId, contentIndex, part });
566
+ }
567
+ }
568
+ beginResponse(response) {
569
+ const id = response.id;
570
+ if (!id || !this.session || this.responses.has(id))
571
+ return;
572
+ if (this.responses.size >= maxResponses) {
573
+ this.untrackedResponses++;
574
+ return;
575
+ }
576
+ const { span } = startSpan("invoke_agent", this.session.ctx);
577
+ commonAttributes(span, this.endpoint);
578
+ span.setAttribute(GEN_AI_OPERATION_NAME, "invoke_agent");
579
+ span.setAttribute(GEN_AI_RESPONSE_ID, id);
580
+ if (this.model)
581
+ span.setAttribute(GEN_AI_REQUEST_MODEL, this.model);
582
+ if (response.conversation_id) {
583
+ span.setAttribute(GEN_AI_CONVERSATION_ID, response.conversation_id);
584
+ }
585
+ this.responses.set(id, {
586
+ span,
587
+ started: performance.now(),
588
+ audioBytes: 0,
589
+ model: this.model,
590
+ outputMessages: new Map(),
591
+ outputParts: new Map(),
592
+ });
593
+ }
594
+ endResponse(response) {
595
+ const id = response.id;
596
+ const tracked = id ? this.responses.get(id) : undefined;
597
+ if (!tracked || !id)
598
+ return;
599
+ this.responses.delete(id);
600
+ const durationInSeconds = (performance.now() - tracked.started) / 1000;
601
+ const { span } = tracked;
602
+ if (response.status)
603
+ span.setAttribute(`${prefix}response.status`, response.status);
604
+ if (response.output_modalities) {
605
+ span.setAttribute(`${prefix}response.output_modalities`, response.output_modalities);
606
+ }
607
+ if (response.usage?.input_tokens !== undefined) {
608
+ span.setAttribute(GEN_AI_USAGE_INPUT_TOKENS, response.usage.input_tokens);
609
+ }
610
+ if (response.usage?.output_tokens !== undefined) {
611
+ span.setAttribute(GEN_AI_USAGE_OUTPUT_TOKENS, response.usage.output_tokens);
612
+ }
613
+ this.flushObservedResponse(tracked, response);
614
+ if (response.status === "failed") {
615
+ span.setAttribute(ERROR_TYPE, "response_failed");
616
+ span.setStatus({ code: SpanStatusCode.ERROR });
617
+ }
618
+ else if (response.status === "completed") {
619
+ span.setStatus({ code: SpanStatusCode.OK });
620
+ }
621
+ const dimensions = {
622
+ operationName: "invoke_agent",
623
+ ...parseEndpoint(this.endpoint),
624
+ responseModel: tracked.model,
625
+ ...(response.status === "failed" ? { errorType: "response_failed" } : {}),
626
+ };
627
+ span.end();
628
+ this.finishedResponses++;
629
+ recordOperationDuration(durationInSeconds, dimensions);
630
+ recordTokenUsage(response.usage?.input_tokens, response.usage?.output_tokens, dimensions);
631
+ }
632
+ flushObservedResponse(tracked, response) {
633
+ const { span } = tracked;
634
+ span.setAttribute(`${prefix}output.audio.bytes`, tracked.audioBytes);
635
+ if (tracked.firstAudioInSeconds !== undefined) {
636
+ span.setAttribute(`${prefix}response.time_to_first_audio_seconds`, tracked.firstAudioInSeconds);
637
+ }
638
+ const fallback = new Map(tracked.outputMessages);
639
+ for (const observed of [...tracked.outputParts.values()].sort((a, b) => a.contentIndex - b.contentIndex)) {
640
+ const message = fallback.get(observed.itemId) ?? {
641
+ role: "assistant",
642
+ parts: [],
643
+ };
644
+ message.parts.push(observed.part);
645
+ fallback.set(observed.itemId, message);
646
+ }
647
+ const output = [];
648
+ let unkeyedFinalItem = false;
649
+ for (const item of response?.output?.slice(0, maxParts) ?? []) {
650
+ const summary = messages(item, this.config.contentRecording);
651
+ if (!summary)
652
+ continue;
653
+ const record = asRecord(item);
654
+ const itemId = record?.type === "function_call" && typeof record.call_id === "string"
655
+ ? `tool:${record.call_id}`
656
+ : typeof record?.id === "string"
657
+ ? record.id
658
+ : typeof record?.call_id === "string"
659
+ ? record.call_id
660
+ : undefined;
661
+ if (itemId) {
662
+ fallback.delete(itemId);
663
+ if (record?.type === "message" && Array.isArray(record.content)) {
664
+ summary.parts = record.content.slice(0, maxParts).flatMap((value, index) => {
665
+ const part = messagePart(value, this.config.contentRecording);
666
+ if (!part)
667
+ return [];
668
+ const observed = tracked.outputParts.get(`${itemId}:${index}:${part.type}`);
669
+ if (part.content === undefined && observed?.part.content !== undefined) {
670
+ part.content = observed.part.content;
671
+ }
672
+ return [part];
673
+ });
674
+ }
675
+ }
676
+ else {
677
+ // Final output is authoritative; don't guess identities for items without an ID.
678
+ unkeyedFinalItem = true;
679
+ }
680
+ output.push(summary);
681
+ }
682
+ if (!unkeyedFinalItem)
683
+ output.push(...fallback.values());
684
+ if (output.length) {
685
+ span.setAttribute(GEN_AI_OUTPUT_MESSAGES, JSON.stringify(output.slice(0, maxParts)));
686
+ }
687
+ }
688
+ close(code, wasClean, error) {
689
+ if (this.ended)
690
+ return;
691
+ this.ended = true;
692
+ recordTelemetry(() => {
693
+ const cancelled = asRecord(error)?.code === "operationCancelled";
694
+ if (this.connectSpan) {
695
+ if (error && !cancelled)
696
+ fail(this.connectSpan, error);
697
+ this.connectSpan.end();
698
+ this.connectSpan = undefined;
699
+ }
700
+ for (const tracked of this.responses.values()) {
701
+ this.flushObservedResponse(tracked);
702
+ tracked.span.setAttribute(`${prefix}response.status`, "interrupted");
703
+ if (error && !cancelled)
704
+ fail(tracked.span, error);
705
+ tracked.span.end();
706
+ recordOperationDuration((performance.now() - tracked.started) / 1000, {
707
+ operationName: "invoke_agent",
708
+ ...parseEndpoint(this.endpoint),
709
+ responseModel: tracked.model,
710
+ ...(error && !cancelled ? { errorType: "response_interrupted" } : {}),
711
+ });
712
+ }
713
+ this.responses.clear();
714
+ this.inputAudioItems.clear();
715
+ this.pendingAudioCommits.items.length = 0;
716
+ this.pendingAudioMessages.items.length = 0;
717
+ this.inputAudioBuffer = undefined;
718
+ if (!this.session)
719
+ return;
720
+ if (error && !cancelled)
721
+ fail(this.session.span, error);
722
+ if (cancelled)
723
+ this.session.span.setAttribute(`${prefix}cancelled`, true);
724
+ if (code !== undefined)
725
+ this.session.span.setAttribute(`${prefix}close.code`, code);
726
+ this.session.span.setAttribute(`${prefix}close.was_clean`, wasClean);
727
+ this.session.span.setAttribute(`${prefix}input.audio.bytes`, this.inputAudioBytes);
728
+ if (this.untrackedInputItems) {
729
+ this.session.span.setAttribute(`${prefix}input.items.untracked`, this.untrackedInputItems);
730
+ }
731
+ this.session.span.setAttribute(`${prefix}responses.finished`, this.finishedResponses);
732
+ if (this.untrackedResponses) {
733
+ this.session.span.setAttribute(`${prefix}responses.untracked`, this.untrackedResponses);
734
+ }
735
+ this.session.span.setAttribute(`${prefix}connected`, this.connected);
736
+ this.session.span.end();
737
+ });
738
+ }
739
+ }
740
+ //# sourceMappingURL=voiceAgentTracing.js.map