@alexkroman1/aai 1.4.5 → 1.5.1

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 (86) hide show
  1. package/.turbo/turbo-build.log +10 -10
  2. package/CHANGELOG.md +19 -0
  3. package/dist/{_internal-types-3p3OJZPb.js → _internal-types-DFL07G3f.js} +2 -0
  4. package/dist/assemblyai-C969QGi4.js +35 -0
  5. package/dist/cartesia-BfQPOQ7Y.js +37 -0
  6. package/dist/host/_pipeline-test-fakes.d.ts +3 -1
  7. package/dist/host/providers/stt/deepgram.d.ts +28 -0
  8. package/dist/host/providers/tts/cartesia.d.ts +1 -1
  9. package/dist/host/providers/tts/rime.d.ts +44 -0
  10. package/dist/host/runtime-barrel.d.ts +4 -2
  11. package/dist/host/runtime-barrel.js +1434 -1209
  12. package/dist/host/runtime.d.ts +2 -2
  13. package/dist/host/s2s.d.ts +16 -16
  14. package/dist/host/session-core.d.ts +37 -0
  15. package/dist/host/transports/pipeline-transport.d.ts +48 -0
  16. package/dist/host/transports/s2s-transport.d.ts +19 -0
  17. package/dist/host/transports/types.d.ts +45 -0
  18. package/dist/host/ws-handler.d.ts +14 -10
  19. package/dist/sdk/_internal-types.d.ts +2 -0
  20. package/dist/sdk/manifest-barrel.js +1 -1
  21. package/dist/sdk/protocol.d.ts +6 -5
  22. package/dist/sdk/providers/llm-barrel.js +1 -1
  23. package/dist/sdk/providers/stt/deepgram.d.ts +35 -0
  24. package/dist/sdk/providers/stt-barrel.d.ts +1 -0
  25. package/dist/sdk/providers/stt-barrel.js +2 -2
  26. package/dist/sdk/providers/tts/cartesia.d.ts +12 -4
  27. package/dist/sdk/providers/tts/rime.d.ts +42 -0
  28. package/dist/sdk/providers/tts-barrel.d.ts +1 -0
  29. package/dist/sdk/providers/tts-barrel.js +2 -2
  30. package/host/_pipeline-test-fakes.ts +6 -3
  31. package/host/_test-utils.ts +209 -128
  32. package/host/builtin-tools.ts +1 -0
  33. package/host/cleanup.test.ts +25 -298
  34. package/host/integration/pipeline-reference.integration.test.ts +30 -35
  35. package/host/providers/resolve.ts +10 -2
  36. package/host/providers/stt/deepgram.test.ts +229 -0
  37. package/host/providers/stt/deepgram.ts +172 -0
  38. package/host/providers/tts/cartesia.ts +7 -3
  39. package/host/providers/tts/rime.test.ts +251 -0
  40. package/host/providers/tts/rime.ts +322 -0
  41. package/host/runtime-barrel.ts +4 -2
  42. package/host/runtime.test.ts +16 -47
  43. package/host/runtime.ts +131 -23
  44. package/host/s2s.test.ts +122 -131
  45. package/host/s2s.ts +44 -52
  46. package/host/session-core.test.ts +257 -0
  47. package/host/session-core.ts +262 -0
  48. package/host/to-vercel-tools.test.ts +9 -1
  49. package/host/transports/pipeline-transport.test.ts +653 -0
  50. package/host/transports/pipeline-transport.ts +532 -0
  51. package/host/{fixture-replay.test.ts → transports/s2s-transport-fixtures.test.ts} +76 -106
  52. package/host/transports/s2s-transport.test.ts +56 -0
  53. package/host/transports/s2s-transport.ts +116 -0
  54. package/host/transports/types.test.ts +22 -0
  55. package/host/transports/types.ts +51 -0
  56. package/host/ws-handler.test.ts +324 -242
  57. package/host/ws-handler.ts +56 -59
  58. package/package.json +2 -1
  59. package/sdk/__snapshots__/exports.test.ts.snap +3 -3
  60. package/sdk/__snapshots__/schema-shapes.test.ts.snap +1 -0
  61. package/sdk/_internal-types.ts +3 -0
  62. package/sdk/protocol-compat.test.ts +8 -0
  63. package/sdk/protocol.ts +6 -5
  64. package/sdk/providers/stt/deepgram.ts +43 -0
  65. package/sdk/providers/stt-barrel.ts +2 -0
  66. package/sdk/providers/tts/cartesia.ts +15 -5
  67. package/sdk/providers/tts/rime.ts +52 -0
  68. package/sdk/providers/tts-barrel.ts +2 -0
  69. package/sdk/schema-alignment.test.ts +18 -6
  70. package/dist/assemblyai-Cxg9eobY.js +0 -18
  71. package/dist/cartesia-DwDk2tEu.js +0 -10
  72. package/dist/host/pipeline-session-ctx.d.ts +0 -24
  73. package/dist/host/pipeline-session.d.ts +0 -52
  74. package/dist/host/session-ctx.d.ts +0 -73
  75. package/dist/host/session.d.ts +0 -62
  76. package/host/pipeline-session-ctx.test.ts +0 -31
  77. package/host/pipeline-session-ctx.ts +0 -36
  78. package/host/pipeline-session.test.ts +0 -672
  79. package/host/pipeline-session.ts +0 -533
  80. package/host/s2s-fixtures.test.ts +0 -237
  81. package/host/session-ctx.test.ts +0 -387
  82. package/host/session-ctx.ts +0 -134
  83. package/host/session-fixture-replay.test.ts +0 -128
  84. package/host/session.test.ts +0 -634
  85. package/host/session.ts +0 -412
  86. /package/dist/{anthropic-BrUCPKUc.js → anthropic-CcLZygAr.js} +0 -0
@@ -2,16 +2,15 @@
2
2
 
3
3
  import { readFileSync } from "node:fs";
4
4
  import { resolve } from "node:path";
5
- import { createNanoEvents } from "nanoevents";
6
5
  import { vi } from "vitest";
7
6
  import type { AgentConfig } from "../sdk/_internal-types.ts";
8
7
  import type { ClientSink } from "../sdk/protocol.ts";
9
8
  import type { AgentDef, ToolContext, ToolDef } from "../sdk/types.ts";
10
9
  import { DEFAULT_SYSTEM_PROMPT } from "../sdk/types.ts";
11
10
  import { createRuntime } from "./runtime.ts";
12
- import type { S2sEvents, S2sHandle } from "./s2s.ts";
13
- import type { Session } from "./session.ts";
14
- import { _internals, type S2sSessionOptions } from "./session.ts";
11
+ import type { ConnectS2sOptions, S2sCallbacks, S2sHandle } from "./s2s.ts";
12
+ import type { SessionCore } from "./session-core.ts";
13
+ import { _internals as s2sTransportInternals } from "./transports/s2s-transport.ts";
15
14
 
16
15
  /** Yield to the microtask queue so pending promises settle. */
17
16
  export function flush(): Promise<void> {
@@ -54,9 +53,12 @@ export function makeConfig(overrides: Partial<AgentConfig> = {}): AgentConfig {
54
53
  };
55
54
  }
56
55
 
57
- /** Create a stub Session with all methods as vi.fn() spies. */
58
- export function makeStubSession(overrides?: Partial<Session>): Session {
56
+ // ─── SessionCore mock ───────────────────────────────────────────────────────
57
+
58
+ /** Create a SessionCore-shaped mock with all methods as vi.fn() spies. */
59
+ export function makeMockCore(overrides?: Partial<SessionCore>): SessionCore {
59
60
  return {
61
+ id: "test",
60
62
  start: vi.fn(() => Promise.resolve()),
61
63
  stop: vi.fn(() => Promise.resolve()),
62
64
  onAudio: vi.fn(),
@@ -64,59 +66,47 @@ export function makeStubSession(overrides?: Partial<Session>): Session {
64
66
  onCancel: vi.fn(),
65
67
  onReset: vi.fn(),
66
68
  onHistory: vi.fn(),
67
- waitForTurn: vi.fn(() => Promise.resolve()),
69
+ onReplyStarted: vi.fn(),
70
+ onReplyDone: vi.fn(),
71
+ onCancelled: vi.fn(),
72
+ onAudioChunk: vi.fn(),
73
+ onAudioDone: vi.fn(),
74
+ onUserTranscript: vi.fn(),
75
+ onAgentTranscript: vi.fn(),
76
+ onToolCall: vi.fn(),
77
+ onError: vi.fn(),
78
+ onSpeechStarted: vi.fn(),
79
+ onSpeechStopped: vi.fn(),
68
80
  ...overrides,
69
81
  };
70
82
  }
71
83
 
72
- // ─── Session test helpers ───────────────────────────────────────────────────
73
-
74
- export type MockS2sHandle = S2sHandle & {
75
- _fire: <K extends keyof S2sEvents>(type: K, ...args: Parameters<S2sEvents[K]>) => void;
76
- };
84
+ // ─── S2sHandle mock ─────────────────────────────────────────────────────────
77
85
 
78
- /** Create a mock S2sHandle backed by nanoevents. */
79
- export function makeMockHandle(): MockS2sHandle {
80
- const emitter = createNanoEvents<S2sEvents>();
86
+ /** Create a mock S2sHandle backed by vi.fn() spies. */
87
+ export function makeMockHandle(): S2sHandle {
81
88
  return {
82
- on: emitter.on.bind(emitter),
83
89
  sendAudio: vi.fn(),
84
90
  sendAudioRaw: vi.fn(),
85
91
  sendToolResult: vi.fn(),
86
92
  updateSession: vi.fn(),
87
93
  resumeSession: vi.fn(),
88
94
  close: vi.fn(),
89
- _fire<K extends keyof S2sEvents>(type: K, ...args: Parameters<S2sEvents[K]>) {
90
- emitter.emit(type, ...args);
91
- },
92
95
  };
93
96
  }
94
97
 
95
- /** Minimal client that tracks events and audio. All methods are vi.fn() spies. */
96
- export function makeClient(): ClientSink & {
97
- events: unknown[];
98
- audioChunks: Uint8Array[];
99
- audioDoneCount: number;
100
- } {
101
- const events: unknown[] = [];
102
- const audioChunks: Uint8Array[] = [];
103
- let audioDoneCount = 0;
98
+ /**
99
+ * Minimal ClientSink stub that satisfies the 3-method interface.
100
+ * All methods are vi.fn() spies. Use in tests that need a valid ClientSink
101
+ * but don't need to inspect event payloads (e.g. routing / creation tests).
102
+ */
103
+ export function makeClientSink(overrides?: Partial<ClientSink>): ClientSink {
104
104
  return {
105
105
  open: true,
106
- events,
107
- audioChunks,
108
- get audioDoneCount() {
109
- return audioDoneCount;
110
- },
111
- event: vi.fn((e: unknown) => {
112
- events.push(e);
113
- }),
114
- playAudioChunk: vi.fn((chunk: Uint8Array) => {
115
- audioChunks.push(chunk);
116
- }),
117
- playAudioDone: vi.fn(() => {
118
- audioDoneCount++;
119
- }),
106
+ event: vi.fn(),
107
+ playAudioChunk: vi.fn(),
108
+ playAudioDone: vi.fn(),
109
+ ...overrides,
120
110
  };
121
111
  }
122
112
 
@@ -132,28 +122,13 @@ export const silentLogger: {
132
122
  debug: vi.fn(),
133
123
  };
134
124
 
135
- export function makeSessionOpts(overrides?: Partial<S2sSessionOptions>): S2sSessionOptions {
136
- return {
137
- id: "session-1",
138
- agent: "test-agent",
139
- client: makeClient(),
140
- agentConfig: {
141
- name: "test-agent",
142
- systemPrompt: DEFAULT_SYSTEM_PROMPT,
143
- greeting: "Hello!",
144
- },
145
- toolSchemas: [],
146
- apiKey: "test-key",
147
- s2sConfig: {
148
- wssUrl: "wss://fake",
149
- inputSampleRate: 16_000,
150
- outputSampleRate: 24_000,
151
- },
152
- executeTool: vi.fn(async () => "tool-result"),
153
- createWebSocket: vi.fn(),
154
- logger: silentLogger,
155
- ...overrides,
156
- };
125
+ /**
126
+ * Fresh logger with per-call `vi.fn()` spies. Use for tests that assert on
127
+ * log output — {@link silentLogger} is a shared singleton and accumulates
128
+ * call history across tests.
129
+ */
130
+ export function makeLogger() {
131
+ return { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() };
157
132
  }
158
133
 
159
134
  // ─── Fixture replay helpers ──────────────────────────────────────────────────
@@ -165,90 +140,179 @@ export function loadFixture<T = Record<string, unknown>[]>(name: string): T {
165
140
  return JSON.parse(readFileSync(resolve(FIXTURE_DIR, name), "utf-8"));
166
141
  }
167
142
 
143
+ // ─── Fixture session helpers (transport-layer spy) ──────────────────────────
144
+
168
145
  /**
169
- * Wire-format event translator: maps a single raw S2S API message to
170
- * a `_fire()` call on the mock handle. Returns false if the message
171
- * type is not dispatchable (audio, content_part, unknown).
146
+ * A tracking ClientSink that records all calls into typed arrays for easy
147
+ * test assertions. Compatible with makeClientSink() but with inspection APIs.
148
+ * Uses the 3-method sink interface — event() dispatches are tracked by type.
172
149
  */
173
- type FireFn = (handle: MockS2sHandle, msg: Record<string, unknown>) => void;
150
+ export type TrackingClientSink = ClientSink & {
151
+ agentTranscripts: string[];
152
+ userTranscripts: string[];
153
+ toolCallEvents: { callId: string; name: string; args: unknown }[];
154
+ audioChunks: Uint8Array[];
155
+ replyDoneCount: number;
156
+ cancelledCount: number;
157
+ speechStartedCount: number;
158
+ speechStoppedCount: number;
159
+ events: import("../sdk/protocol.ts").ClientEvent[];
160
+ };
161
+
162
+ export function makeTrackingClient(): TrackingClientSink {
163
+ const agentTranscripts: string[] = [];
164
+ const userTranscripts: string[] = [];
165
+ const toolCallEvents: { callId: string; name: string; args: unknown }[] = [];
166
+ const audioChunks: Uint8Array[] = [];
167
+ const events: import("../sdk/protocol.ts").ClientEvent[] = [];
168
+ let replyDoneCount = 0;
169
+ let cancelledCount = 0;
170
+ let speechStartedCount = 0;
171
+ let speechStoppedCount = 0;
174
172
 
175
- const FIXTURE_DISPATCH: Record<string, FireFn> = {
176
- "session.ready": (h, m) => h._fire("ready", { sessionId: m.session_id as string }),
177
- "session.updated": () => {
178
- /* dropped — no longer dispatched */
179
- },
180
- "session.error": (h, m) => {
181
- const code = m.code as string;
182
- if (code === "session_not_found" || code === "session_forbidden") h._fire("sessionExpired");
183
- else h._fire("error", new Error(m.message as string));
184
- },
185
- error: (h, m) => h._fire("error", new Error(m.message as string)),
186
- "input.speech.started": (h) => h._fire("event", { type: "speech_started" }),
187
- "input.speech.stopped": (h) => h._fire("event", { type: "speech_stopped" }),
188
- "transcript.user": (h, m) =>
189
- h._fire("event", { type: "user_transcript", text: m.text as string }),
190
- "reply.started": (h, m) => h._fire("replyStarted", { replyId: (m.reply_id as string) ?? "" }),
191
- "transcript.agent": (h, m) =>
192
- h._fire("event", {
193
- type: "agent_transcript",
194
- text: (m.text as string) ?? "",
195
- _interrupted: m.interrupted === true,
173
+ return {
174
+ open: true,
175
+ agentTranscripts,
176
+ userTranscripts,
177
+ toolCallEvents,
178
+ audioChunks,
179
+ events,
180
+ get replyDoneCount() {
181
+ return replyDoneCount;
182
+ },
183
+ get cancelledCount() {
184
+ return cancelledCount;
185
+ },
186
+ get speechStartedCount() {
187
+ return speechStartedCount;
188
+ },
189
+ get speechStoppedCount() {
190
+ return speechStoppedCount;
191
+ },
192
+ event: vi.fn((e: import("../sdk/protocol.ts").ClientEvent) => {
193
+ events.push(e);
194
+ switch (e.type) {
195
+ case "agent_transcript":
196
+ agentTranscripts.push(e.text);
197
+ break;
198
+ case "user_transcript":
199
+ userTranscripts.push(e.text);
200
+ break;
201
+ case "tool_call":
202
+ toolCallEvents.push({ callId: e.toolCallId, name: e.toolName, args: e.args });
203
+ break;
204
+ case "reply_done":
205
+ replyDoneCount++;
206
+ break;
207
+ case "cancelled":
208
+ cancelledCount++;
209
+ break;
210
+ case "speech_started":
211
+ speechStartedCount++;
212
+ break;
213
+ case "speech_stopped":
214
+ speechStoppedCount++;
215
+ break;
216
+ default:
217
+ break;
218
+ }
196
219
  }),
197
- "tool.call": (h, m) =>
198
- h._fire("event", {
199
- type: "tool_call",
200
- toolCallId: m.call_id as string,
201
- toolName: m.name as string,
202
- args: (m.args as Record<string, unknown>) ?? {},
220
+ playAudioChunk: vi.fn((chunk: Uint8Array) => {
221
+ audioChunks.push(chunk);
203
222
  }),
204
- "reply.done": (h, m) => {
205
- if (m.status === "interrupted") h._fire("event", { type: "cancelled" });
206
- else h._fire("event", { type: "reply_done" });
207
- },
208
- };
223
+ playAudioDone: vi.fn(),
224
+ };
225
+ }
209
226
 
210
227
  /**
211
- * Replay recorded S2S API messages through a MockS2sHandle.
212
- *
213
- * Converts raw wire-format JSON (from fixtures/) into typed `_fire()` calls.
214
- * This is the inverse of `dispatchS2sMessage` in s2s.ts — it translates
215
- * snake_case API fields to camelCase event payloads.
216
- *
217
- * Messages that don't map to an event (audio, `reply.content_part.*`) are skipped.
228
+ * Translate a single fixture wire-format message directly into S2sCallbacks calls.
229
+ * This is the callback-based equivalent of the old FIXTURE_DISPATCH / replayFixtureMessages.
218
230
  */
219
- export function replayFixtureMessages(
220
- handle: MockS2sHandle,
221
- messages: Record<string, unknown>[],
222
- ): void {
223
- for (const msg of messages) {
224
- FIXTURE_DISPATCH[msg.type as string]?.(handle, msg);
231
+ export function fireFixtureMessage(callbacks: S2sCallbacks, msg: Record<string, unknown>): void {
232
+ switch (msg.type) {
233
+ case "session.ready":
234
+ callbacks.onSessionReady(msg.session_id as string);
235
+ break;
236
+ case "session.updated":
237
+ break; // no callback
238
+ case "reply.started":
239
+ callbacks.onReplyStarted(msg.reply_id as string);
240
+ break;
241
+ case "reply.done":
242
+ if (msg.status === "interrupted") callbacks.onCancelled();
243
+ else callbacks.onReplyDone();
244
+ break;
245
+ case "transcript.user":
246
+ callbacks.onUserTranscript(msg.text as string);
247
+ break;
248
+ case "transcript.agent":
249
+ callbacks.onAgentTranscript(msg.text as string, Boolean(msg.interrupted));
250
+ break;
251
+ case "tool.call":
252
+ callbacks.onToolCall(
253
+ msg.call_id as string,
254
+ msg.name as string,
255
+ (msg.args ?? {}) as Record<string, unknown>,
256
+ );
257
+ break;
258
+ case "input.speech.started":
259
+ callbacks.onSpeechStarted();
260
+ break;
261
+ case "input.speech.stopped":
262
+ callbacks.onSpeechStopped();
263
+ break;
264
+ case "session.error": {
265
+ const code = msg.code as string;
266
+ if (code === "session_not_found" || code === "session_forbidden")
267
+ callbacks.onSessionExpired();
268
+ else callbacks.onError(new Error((msg.message ?? "session error") as string));
269
+ break;
270
+ }
271
+ case "error":
272
+ callbacks.onError(new Error((msg.message ?? "error") as string));
273
+ break;
274
+ case "reply.audio":
275
+ break; // skip — audio tested separately
276
+ default:
277
+ break;
225
278
  }
226
279
  }
227
280
 
228
- // ─── Real-executor fixture replay ────────────────────────────────────────────
229
-
230
281
  /**
231
282
  * Create a real Runtime-backed session for fixture replay testing.
232
283
  *
233
- * Uses a real `Runtime` (real tool execution, real hooks) but replaces the
234
- * S2S WebSocket with a mock handle so fixture messages can be replayed
235
- * through the full orchestration layer.
284
+ * Spies on s2s-transport.ts `_internals.connectS2s` (the transport-layer seam
285
+ * added in Task 15) so that captured S2sCallbacks can be fired directly —
286
+ * no nanoevents, no old S2sEvents system.
236
287
  *
237
- * Exercises: AgentDef toAgentConfig tool schemas Zod arg validation
238
- * executeToolCall session orchestration (reply guards, tool buffering,
239
- * turnPromise chaining).
288
+ * Call `await ctx.start()` first to trigger the spy, then `ctx.replay(name)`
289
+ * or fire `ctx.mockCallbacks.on*` directly.
240
290
  *
241
- * Call `cleanup()` when done to restore the connectS2s spy.
291
+ * Call `cleanup()` in afterEach to restore the spy.
242
292
  */
243
293
  export function createFixtureSession(
244
294
  // biome-ignore lint/suspicious/noExplicitAny: test helper accepts any agent state type
245
295
  agent: AgentDef<any>,
246
296
  opts?: { env?: Record<string, string> },
247
297
  ) {
248
- const mockHandle = makeMockHandle();
249
- const connectSpy = vi.spyOn(_internals, "connectS2s").mockResolvedValue(mockHandle);
250
- const client = makeClient();
298
+ let capturedCallbacks: S2sCallbacks | null = null;
299
+ const fakeHandle: S2sHandle = {
300
+ sendAudio: vi.fn(),
301
+ sendAudioRaw: vi.fn(),
302
+ sendToolResult: vi.fn(),
303
+ updateSession: vi.fn(),
304
+ resumeSession: vi.fn(),
305
+ close: vi.fn(),
306
+ };
251
307
 
308
+ const connectSpy = vi
309
+ .spyOn(s2sTransportInternals, "connectS2s")
310
+ .mockImplementation(async (connectOpts: ConnectS2sOptions) => {
311
+ capturedCallbacks = connectOpts.callbacks;
312
+ return fakeHandle;
313
+ });
314
+
315
+ const client = makeTrackingClient();
252
316
  const executor = createRuntime({
253
317
  agent,
254
318
  env: opts?.env ?? {},
@@ -261,14 +325,31 @@ export function createFixtureSession(
261
325
  client,
262
326
  });
263
327
 
328
+ function getCallbacks(): S2sCallbacks {
329
+ if (!capturedCallbacks) throw new Error("must call start() before accessing callbacks");
330
+ return capturedCallbacks;
331
+ }
332
+
264
333
  return {
265
334
  session,
266
335
  client,
267
- mockHandle,
336
+ fakeHandle,
268
337
  executor,
269
- /** Replay a fixture file through the session's S2S handle. */
338
+ /** Trigger transport.start() fires the connectS2s spy and captures callbacks. */
339
+ async start() {
340
+ await session.start();
341
+ if (!capturedCallbacks) throw new Error("connectS2s was never called during start()");
342
+ },
343
+ /** Direct access to the captured S2sCallbacks for manual event firing. */
344
+ get mockCallbacks(): S2sCallbacks {
345
+ return getCallbacks();
346
+ },
347
+ /** Replay a fixture file by translating each message to S2sCallbacks calls. */
270
348
  replay(fixtureName: string) {
271
- replayFixtureMessages(mockHandle, loadFixture(fixtureName));
349
+ const cbs = getCallbacks();
350
+ for (const msg of loadFixture(fixtureName)) {
351
+ fireFixtureMessage(cbs, msg as Record<string, unknown>);
352
+ }
272
353
  },
273
354
  /** Restore the connectS2s spy. Call in afterEach. */
274
355
  cleanup() {
@@ -242,6 +242,7 @@ export function resolveAllBuiltins(
242
242
  for (const [toolName, def] of resolveBuiltin(name, opts)) {
243
243
  defs[toolName] = def;
244
244
  schemas.push({
245
+ type: "function",
245
246
  name: toolName,
246
247
  description: def.description,
247
248
  parameters: z.toJSONSchema(def.parameters ?? EMPTY_PARAMS) as ToolSchema["parameters"],