@gea-ai/agent-sdk 0.1.260916-alpha.2 → 0.1.260917-alpha.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.
@@ -19,19 +19,6 @@ export declare function executeChannelBehavior(input: {
19
19
  environment: AgentChannelFetchEnvironment;
20
20
  signal: AbortSignal;
21
21
  }): Promise<{
22
- sequence: number;
23
- duplicate: boolean;
24
- } | {
25
- state: AgentChannelJson;
26
- intents: {
27
- key: string;
28
- payload: AgentChannelJson;
29
- eventId: string;
30
- operationId: string;
31
- }[];
32
- cursor: number;
33
- idempotencyWindowMs?: number | undefined;
34
- } | {
35
22
  status: "sent";
36
23
  receipt?: AgentChannelJson | undefined;
37
24
  } | {
@@ -56,6 +43,19 @@ export declare function executeChannelBehavior(input: {
56
43
  message: string;
57
44
  retryable: boolean;
58
45
  };
46
+ } | {
47
+ sequence: number;
48
+ duplicate: boolean;
49
+ } | {
50
+ state: AgentChannelJson;
51
+ intents: {
52
+ key: string;
53
+ payload: AgentChannelJson;
54
+ eventId: string;
55
+ operationId: string;
56
+ }[];
57
+ cursor: number;
58
+ idempotencyWindowMs?: number | undefined;
59
59
  } | {
60
60
  events: {
61
61
  id: string;
Binary file
@@ -18,6 +18,7 @@ import { type AgentChannelJournalAppend } from "@gea-ai/contract/agent-channel-r
18
18
  import { type AgentDurableObjectNamespace, type AgentToolApprovalDecision, type AgentToolIdentity, type AgentToolRuntimeSdk, type DefinedAgentPackage } from "./index";
19
19
  import { type AgentChannelAddressEnvironment } from "./agent-channel-worker";
20
20
  export { handleAgentChannelAddressAlarm, handleAgentChannelAddressRequest, type AgentChannelAddressEnvironment, type AgentChannelAddressStorage, type AgentChannelAddressTransactionStorage, } from "./agent-channel-worker";
21
+ declare const jsonObjectSchema: z.ZodRecord<z.ZodString, z.ZodJSONSchema>;
21
22
  export type AgentHostBinding = {
22
23
  fetch(path: string, init?: RequestInit): Promise<Response>;
23
24
  };
@@ -191,7 +192,7 @@ export type AgentSessionNamespace = {
191
192
  };
192
193
  idFromName(name: string): unknown;
193
194
  };
194
- export declare function createGeaAI(host: AgentHostBinding, publicModel: string, extraBody?: Record<string, unknown>, client?: AgentModelProxyClient): Promise<LanguageModel>;
195
+ export declare function createGeaAI(host: AgentHostBinding, publicModel: string, extraBody?: Record<string, unknown>, client?: AgentModelProxyClient, requestBody?: z.infer<typeof jsonObjectSchema>): Promise<LanguageModel>;
195
196
  export declare function runAgentWorkerRequest(input: {
196
197
  interactionPolicy?: AgentInteractionPolicy;
197
198
  initialization?: AgentWorkerToolInitialization;
@@ -1,3 +1,5 @@
1
+ import { moveOpenAICompatibleCacheControlToMessageContent as moveOpenAiCompatibleCacheControlToContent, createCatalogLanguageModel, } from "./catalog-language-model.js";
2
+ import { mergeLlmJson, llmRuntimeRequestOptionsSchema, } from "@gea-ai/contract/llm-catalog";
1
3
  import { agentWorkerAttachmentOutputSchema } from "@gea-ai/contract/agent-api-host";
2
4
  import { streamManagedAgentSessionRun } from "./agent-session-stream.js";
3
5
  import { agentSessionReadToolOutputSchema, agentSessionReadToolOutputResultSchema, } from "@gea-ai/contract/agent-session";
@@ -60,12 +62,7 @@ const modelDescriptorSchema = z.union([
60
62
  z.object({ transport: z.literal(AGENT_MODEL_PROXY_TRANSPORT) }).strict(),
61
63
  ]);
62
64
  const jsonObjectSchema = z.record(z.string(), z.json());
63
- const modelRequestOptionsSchema = z
64
- .object({
65
- extraBody: jsonObjectSchema.optional(),
66
- providerOptions: z.record(z.string(), jsonObjectSchema).optional(),
67
- })
68
- .strict();
65
+ const modelRequestOptionsSchema = llmRuntimeRequestOptionsSchema;
69
66
  function mergeModelProviderOptions(defaults, overrides) {
70
67
  const merged = { ...defaults };
71
68
  for (const [provider, options] of Object.entries(overrides ?? {})) {
@@ -2373,50 +2370,6 @@ function mergeRequestBodyExtra(body, extraBody) {
2373
2370
  }
2374
2371
  return merged;
2375
2372
  }
2376
- function moveOpenAiCompatibleCacheControlToContent(body) {
2377
- if (!Array.isArray(body.messages))
2378
- return body;
2379
- return {
2380
- ...body,
2381
- messages: body.messages.map((message) => {
2382
- if (!isJsonObject(message) ||
2383
- !("cache_control" in message) ||
2384
- message.cache_control == null) {
2385
- return message;
2386
- }
2387
- const { cache_control: cacheControl, content, ...rest } = message;
2388
- if (typeof content === "string") {
2389
- return {
2390
- ...rest,
2391
- content: [
2392
- { cache_control: cacheControl, text: content, type: "text" },
2393
- ],
2394
- };
2395
- }
2396
- if (!Array.isArray(content))
2397
- return message;
2398
- if (content.some((part) => isJsonObject(part) && "cache_control" in part)) {
2399
- return { ...rest, content };
2400
- }
2401
- let textPartIndex = -1;
2402
- for (let index = content.length - 1; index >= 0; index -= 1) {
2403
- const part = content[index];
2404
- if (isJsonObject(part) && part.type === "text") {
2405
- textPartIndex = index;
2406
- break;
2407
- }
2408
- }
2409
- return textPartIndex < 0
2410
- ? message
2411
- : {
2412
- ...rest,
2413
- content: content.map((part, index) => index === textPartIndex && isJsonObject(part)
2414
- ? { ...part, cache_control: cacheControl }
2415
- : part),
2416
- };
2417
- }),
2418
- };
2419
- }
2420
2373
  function serializeHostedModelCallOptions(options) {
2421
2374
  const { abortSignal: _abortSignal, ...serializableOptions } = options;
2422
2375
  return serializableOptions;
@@ -2501,12 +2454,13 @@ function createHostedGeaLanguageModel(input) {
2501
2454
  };
2502
2455
  }
2503
2456
  async function createGeaCoreConnection(host, publicModel, requestOptions, signal, maxOutputTokens) {
2504
- const descriptor = modelDescriptorSchema.parse(await requireHostJson(host, `/model?model=${encodeURIComponent(publicModel)}&transport=agent-core&includeMaxOutputTokens=true`, { signal }));
2457
+ const descriptor = modelDescriptorSchema.parse(await requireHostJson(host, `/model?model=${encodeURIComponent(publicModel)}&transport=agent-core&includeMaxOutputTokens=true&includeCatalogConfig=true`, { signal }));
2505
2458
  if ("transport" in descriptor)
2506
2459
  throw new Error("Agent Core requires the raw model transport.");
2507
- const format = descriptor.providerType === "anthropic"
2508
- ? "anthropic-messages"
2509
- : "openai-completions";
2460
+ const format = descriptor.format ??
2461
+ (descriptor.providerType === "anthropic"
2462
+ ? "anthropic-messages"
2463
+ : "openai-completions");
2510
2464
  const resolvedMaxOutputTokens = maxOutputTokens ?? descriptor.maxOutputTokens;
2511
2465
  const provider = agentCoreProviderConfigSchema.parse({
2512
2466
  format,
@@ -2516,10 +2470,14 @@ async function createGeaCoreConnection(host, publicModel, requestOptions, signal
2516
2470
  descriptor.includeUsage === false
2517
2471
  ? { stream_options: { include_usage: false } }
2518
2472
  : {}),
2519
- ...descriptor.requestOptions?.extraBody,
2520
- ...requestOptions?.extraBody,
2473
+ ...mergeLlmJson(requestOptions?.requestBody ??
2474
+ descriptor.requestOptions?.requestBody ??
2475
+ descriptor.requestOptions?.extraBody, requestOptions?.extraBody),
2521
2476
  },
2522
2477
  generation: {
2478
+ ...(descriptor.compat?.maxTokensField
2479
+ ? { max_tokens_field: descriptor.compat.maxTokensField }
2480
+ : {}),
2523
2481
  ...fromAiSdkProviderOptions(format, mergeModelProviderOptions(descriptor.requestOptions?.providerOptions, requestOptions?.providerOptions)),
2524
2482
  ...(resolvedMaxOutputTokens === undefined
2525
2483
  ? {}
@@ -2530,7 +2488,7 @@ async function createGeaCoreConnection(host, publicModel, requestOptions, signal
2530
2488
  if (typeof init?.body !== "string")
2531
2489
  throw new Error("Core provider request must contain JSON text.");
2532
2490
  const headers = new Headers(init.headers);
2533
- return host.fetch(`${HOST_PATH}/model/provider-fetch?model=${encodeURIComponent(publicModel)}`, {
2491
+ return host.fetch(`${HOST_PATH}/model/provider-fetch?model=${encodeURIComponent(publicModel)}&includeCatalogConfig=true`, {
2534
2492
  method: "POST",
2535
2493
  body: init.body,
2536
2494
  signal: init.signal,
@@ -2545,14 +2503,16 @@ async function createGeaCoreConnection(host, publicModel, requestOptions, signal
2545
2503
  return {
2546
2504
  provider,
2547
2505
  fetch,
2548
- cacheControlProvider: descriptor.cacheControlProvider ??
2549
- (descriptor.providerType === "anthropic"
2550
- ? "anthropic"
2551
- : undefined),
2506
+ cacheControlProvider: descriptor.cacheControl === "off"
2507
+ ? undefined
2508
+ : (descriptor.cacheControlProvider ??
2509
+ (descriptor.providerType === "anthropic"
2510
+ ? "anthropic"
2511
+ : undefined)),
2552
2512
  };
2553
2513
  }
2554
- export async function createGeaAI(host, publicModel, extraBody, client) {
2555
- const descriptor = modelDescriptorSchema.parse(await requireHostJson(host, `/model?model=${encodeURIComponent(publicModel)}&includeModelId=true&includeMaxOutputTokens=true`));
2514
+ export async function createGeaAI(host, publicModel, extraBody, client, requestBody) {
2515
+ const descriptor = modelDescriptorSchema.parse(await requireHostJson(host, `/model?model=${encodeURIComponent(publicModel)}&includeModelId=true&includeMaxOutputTokens=true&includeCatalogConfig=true`));
2556
2516
  if ("transport" in descriptor) {
2557
2517
  return createHostedGeaLanguageModel({ client, host, publicModel });
2558
2518
  }
@@ -2562,7 +2522,7 @@ export async function createGeaAI(host, publicModel, extraBody, client) {
2562
2522
  }
2563
2523
  const providerHeaders = new Headers(init.headers);
2564
2524
  const anthropicBeta = providerHeaders.get("anthropic-beta");
2565
- return await host.fetch(`${HOST_PATH}/model/provider-fetch?model=${encodeURIComponent(publicModel)}`, {
2525
+ return await host.fetch(`${HOST_PATH}/model/provider-fetch?model=${encodeURIComponent(publicModel)}&includeCatalogConfig=true`, {
2566
2526
  body: init.body,
2567
2527
  headers: {
2568
2528
  ...(anthropicBeta ? { "anthropic-beta": anthropicBeta } : {}),
@@ -2572,6 +2532,35 @@ export async function createGeaAI(host, publicModel, extraBody, client) {
2572
2532
  signal: init.signal,
2573
2533
  });
2574
2534
  };
2535
+ if (descriptor.format) {
2536
+ const model = createCatalogLanguageModel({
2537
+ format: descriptor.format,
2538
+ modelId: descriptor.modelId ?? publicModel,
2539
+ baseURL: "https://gea-ai-binding.invalid/v1",
2540
+ apiKey: "host-managed",
2541
+ auth: "bearer",
2542
+ fetch: providerFetch,
2543
+ includeUsage: descriptor.includeUsage ?? undefined,
2544
+ maxOutputTokens: descriptor.maxOutputTokens,
2545
+ maxTokensField: descriptor.compat?.maxTokensField,
2546
+ requestBody: mergeLlmJson(requestBody ??
2547
+ descriptor.requestOptions?.requestBody ??
2548
+ descriptor.requestOptions?.extraBody, jsonObjectSchema.parse(extraBody ?? {})),
2549
+ });
2550
+ const cache = descriptor.cacheControlProvider;
2551
+ const state = {};
2552
+ return wrapLanguageModel({
2553
+ model,
2554
+ modelId: publicModel,
2555
+ middleware: {
2556
+ transformParams: async ({ params }) => ({
2557
+ ...params,
2558
+ prompt: addCacheControlToMessages(params.prompt, cache, state),
2559
+ tools: addCacheControlToToolList(params.tools, cache, state),
2560
+ }),
2561
+ },
2562
+ });
2563
+ }
2575
2564
  if (descriptor.providerType === "anthropic") {
2576
2565
  if (extraBody && Object.keys(extraBody).length > 0) {
2577
2566
  throw new Error("Anthropic models do not support extra request body fields.");
@@ -3643,7 +3632,7 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
3643
3632
  : await createGeaAI(host, configuredModel, requestOptions.extraBody, {
3644
3633
  kind: "agent-dev",
3645
3634
  packageName: input.snapshot.name,
3646
- });
3635
+ }, requestOptions.requestBody);
3647
3636
  const modelMessages = replaceInputFiles(await convertToModelMessages(payload.originalMessages), inputFiles);
3648
3637
  // Model file parts can contain URL and byte values that ordinary JSON cannot preserve.
3649
3638
  const runtimeContext = decodeAgentModelProxyValue(payload.runtimeContext);
@@ -3947,7 +3936,9 @@ async function executeAgentWorkerRequest(input, waitUntil, ownComputer, ownSessi
3947
3936
  let resolved = models.get(modelId);
3948
3937
  if (!resolved) {
3949
3938
  // TS strategies choose their own model features, independently of Core.
3950
- resolved = createGeaAI(host, modelId, modelId === configuredModel ? requestOptions.extraBody : undefined, { kind: "agent-dev", packageName: input.snapshot.name });
3939
+ resolved = createGeaAI(host, modelId, modelId === configuredModel ? requestOptions.extraBody : undefined, { kind: "agent-dev", packageName: input.snapshot.name }, modelId === configuredModel
3940
+ ? requestOptions.requestBody
3941
+ : undefined);
3951
3942
  models.set(modelId, resolved);
3952
3943
  }
3953
3944
  return resolved;
@@ -0,0 +1,17 @@
1
+ export { moveOpenAICompatibleCacheControlToMessageContent } from "./openai-compatible-cache-control";
2
+ import { type LlmModelFormat } from "@gea-ai/contract/llm-catalog";
3
+ import { z } from "zod";
4
+ declare const object: z.ZodRecord<z.ZodString, z.ZodJSONSchema>;
5
+ /** A shared AI SDK adapter for hosted and native Catalog v2 execution. */
6
+ export declare function createCatalogLanguageModel(config: {
7
+ format: LlmModelFormat;
8
+ modelId: string;
9
+ baseURL: string;
10
+ apiKey: string;
11
+ auth: "bearer" | "x-api-key";
12
+ fetch: typeof globalThis.fetch;
13
+ includeUsage?: boolean;
14
+ maxOutputTokens?: number;
15
+ maxTokensField?: "max_tokens" | "max_completion_tokens";
16
+ requestBody?: z.infer<typeof object>;
17
+ }): import("@ai-sdk/provider").LanguageModelV4;
@@ -0,0 +1,231 @@
1
+ import { moveOpenAICompatibleCacheControlToMessageContent } from "./openai-compatible-cache-control.js";
2
+ export { moveOpenAICompatibleCacheControlToMessageContent } from "./openai-compatible-cache-control.js";
3
+ import { createAnthropic } from "@ai-sdk/anthropic";
4
+ import { createOpenAI } from "@ai-sdk/openai";
5
+ import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
6
+ import { wrapLanguageModel } from "ai";
7
+ import { resolveAnthropicModelId } from "@gea-ai/contract/model-identity";
8
+ import { llmRequestBodySchema, mergeLlmJson, } from "@gea-ai/contract/llm-catalog";
9
+ import { z } from "zod";
10
+ const object = z.record(z.string(), z.json());
11
+ const controlsSchema = z.object({
12
+ thinking: z
13
+ .object({
14
+ type: z.string(),
15
+ budget_tokens: z.number().optional(),
16
+ display: z.string().optional(),
17
+ })
18
+ .optional(),
19
+ output_config: z.object({ effort: z.string().optional() }).optional(),
20
+ reasoning: z
21
+ .object({ effort: z.string().optional(), summary: z.string().optional() })
22
+ .optional(),
23
+ reasoning_effort: z.string().optional(),
24
+ temperature: z.number().optional(),
25
+ top_p: z.number().optional(),
26
+ presence_penalty: z.number().optional(),
27
+ frequency_penalty: z.number().optional(),
28
+ seed: z.number().int().optional(),
29
+ store: z.boolean().optional(),
30
+ });
31
+ const usageSchema = z.object({
32
+ input_tokens_details: z
33
+ .object({ cache_write_tokens: z.number().nonnegative().optional() })
34
+ .nullish(),
35
+ prompt_tokens_details: z
36
+ .object({
37
+ cache_creation_input_tokens: z.number().nonnegative().optional(),
38
+ })
39
+ .nullish(),
40
+ });
41
+ const usageFrameSchema = z.object({
42
+ usage: usageSchema.nullish(),
43
+ response: z.object({ usage: usageSchema.nullish() }).optional(),
44
+ });
45
+ /** A shared AI SDK adapter for hosted and native Catalog v2 execution. */
46
+ export function createCatalogLanguageModel(config) {
47
+ const bodyDefaults = llmRequestBodySchema.parse(config.requestBody ?? {});
48
+ const controls = controlsSchema.parse(bodyDefaults);
49
+ const namespace = config.format === "anthropic-messages"
50
+ ? "anthropic"
51
+ : config.format === "openai-responses"
52
+ ? "openai"
53
+ : "openaiCompatible";
54
+ const providerDefaults = {};
55
+ if (config.format === "anthropic-messages") {
56
+ if (controls.thinking) {
57
+ const { budget_tokens, ...thinking } = controls.thinking;
58
+ providerDefaults.thinking = {
59
+ ...thinking,
60
+ ...(budget_tokens === undefined ? {} : { budgetTokens: budget_tokens }),
61
+ };
62
+ }
63
+ if (controls.output_config?.effort)
64
+ providerDefaults.effort = controls.output_config.effort;
65
+ }
66
+ else if (config.format === "openai-responses") {
67
+ providerDefaults.store = controls.store ?? false;
68
+ if (controls.reasoning) {
69
+ providerDefaults.forceReasoning = true;
70
+ if (controls.reasoning.effort)
71
+ providerDefaults.reasoningEffort = controls.reasoning.effort;
72
+ if (controls.reasoning.summary)
73
+ providerDefaults.reasoningSummary = controls.reasoning.summary;
74
+ }
75
+ }
76
+ else if (controls.reasoning_effort)
77
+ providerDefaults.reasoningEffort = controls.reasoning_effort;
78
+ // Mapped controls must not be merged back after SDK serialization: doing so
79
+ // can reintroduce a default thinking budget after an explicit mode change.
80
+ const extensions = { ...bodyDefaults };
81
+ for (const key of [
82
+ "temperature",
83
+ "top_p",
84
+ "presence_penalty",
85
+ "frequency_penalty",
86
+ "seed",
87
+ ])
88
+ delete extensions[key];
89
+ if (config.format === "anthropic-messages") {
90
+ if (extensions.thinking) {
91
+ const { type: _type, budget_tokens: _budget, display: _display, ...rest } = object.parse(extensions.thinking);
92
+ if (Object.keys(rest).length)
93
+ extensions.thinking = rest;
94
+ else
95
+ delete extensions.thinking;
96
+ }
97
+ if (extensions.output_config) {
98
+ const { effort: _effort, ...rest } = object.parse(extensions.output_config);
99
+ extensions.output_config = rest;
100
+ }
101
+ }
102
+ else if (config.format === "openai-responses") {
103
+ delete extensions.store;
104
+ if (extensions.reasoning) {
105
+ const { effort: _effort, summary: _summary, ...rest } = object.parse(extensions.reasoning);
106
+ extensions.reasoning = rest;
107
+ }
108
+ }
109
+ else
110
+ delete extensions.reasoning_effort;
111
+ const fetch = async (input, init) => {
112
+ if (typeof init?.body !== "string")
113
+ throw new Error("Catalog provider request must be JSON text.");
114
+ // SDK encoding owns messages, signatures and tools. Merge only parameter defaults.
115
+ const body = mergeLlmJson(extensions, object.parse(JSON.parse(init.body)));
116
+ body.model = config.modelId;
117
+ if (config.format === "openai-completions" &&
118
+ config.maxTokensField === "max_completion_tokens" &&
119
+ body.max_tokens !== undefined) {
120
+ body.max_completion_tokens = body.max_tokens;
121
+ delete body.max_tokens;
122
+ }
123
+ const headers = new Headers(init.headers);
124
+ headers.delete("authorization");
125
+ headers.delete("x-api-key");
126
+ headers.set(config.auth === "bearer" ? "authorization" : "x-api-key", config.auth === "bearer" ? `Bearer ${config.apiKey}` : config.apiKey);
127
+ return config.fetch(input, {
128
+ ...init,
129
+ headers,
130
+ body: JSON.stringify(body),
131
+ });
132
+ };
133
+ const connection = { baseURL: config.baseURL, apiKey: config.apiKey, fetch };
134
+ const model = config.format === "anthropic-messages"
135
+ ? createAnthropic({
136
+ ...connection,
137
+ ...(config.auth === "bearer"
138
+ ? { apiKey: undefined, authToken: config.apiKey }
139
+ : {}),
140
+ })(resolveAnthropicModelId(config.modelId))
141
+ : config.format === "openai-responses"
142
+ ? createOpenAI(connection).responses(config.modelId)
143
+ : createOpenAICompatible({
144
+ ...connection,
145
+ name: "openaiCompatible",
146
+ includeUsage: config.includeUsage,
147
+ transformRequestBody: moveOpenAICompatibleCacheControlToMessageContent,
148
+ })(config.modelId);
149
+ function withCacheWrites(usage, raw) {
150
+ const parsed = usageSchema.safeParse(raw);
151
+ const write = parsed.success
152
+ ? (parsed.data.input_tokens_details?.cache_write_tokens ??
153
+ parsed.data.prompt_tokens_details?.cache_creation_input_tokens)
154
+ : undefined;
155
+ if (write === undefined)
156
+ return usage;
157
+ return {
158
+ ...usage,
159
+ inputTokens: {
160
+ ...usage.inputTokens,
161
+ cacheWrite: write,
162
+ noCache: usage.inputTokens.total === undefined
163
+ ? undefined
164
+ : Math.max(0, usage.inputTokens.total -
165
+ (usage.inputTokens.cacheRead ?? 0) -
166
+ write),
167
+ },
168
+ };
169
+ }
170
+ return wrapLanguageModel({
171
+ model,
172
+ modelId: config.modelId,
173
+ middleware: {
174
+ transformParams: async ({ params }) => {
175
+ const overrides = object.parse(params.providerOptions?.[namespace] ?? {});
176
+ const options = { ...providerDefaults, ...overrides };
177
+ if (config.format === "openai-responses" &&
178
+ options.reasoningEffort !== undefined)
179
+ options.forceReasoning ??= true;
180
+ return {
181
+ ...params,
182
+ maxOutputTokens: params.maxOutputTokens ?? config.maxOutputTokens,
183
+ temperature: params.temperature ?? controls.temperature,
184
+ topP: params.topP ?? controls.top_p,
185
+ presencePenalty: params.presencePenalty ?? controls.presence_penalty,
186
+ frequencyPenalty: params.frequencyPenalty ?? controls.frequency_penalty,
187
+ seed: params.seed ?? controls.seed,
188
+ providerOptions: { ...params.providerOptions, [namespace]: options },
189
+ };
190
+ },
191
+ wrapGenerate: async ({ doGenerate }) => {
192
+ const result = await doGenerate();
193
+ const raw = usageFrameSchema.safeParse(result.response?.body);
194
+ return {
195
+ ...result,
196
+ usage: withCacheWrites(result.usage, raw.success ? raw.data.usage : undefined),
197
+ };
198
+ },
199
+ wrapStream: async ({ model: providerModel, params }) => {
200
+ const result = await providerModel.doStream({
201
+ ...params,
202
+ includeRawChunks: true,
203
+ });
204
+ let rawUsage;
205
+ return {
206
+ ...result,
207
+ stream: result.stream.pipeThrough(new TransformStream({
208
+ transform(chunk, controller) {
209
+ if (chunk.type === "raw") {
210
+ const frame = usageFrameSchema.safeParse(chunk.rawValue);
211
+ if (frame.success)
212
+ rawUsage =
213
+ frame.data.usage ??
214
+ frame.data.response?.usage ??
215
+ rawUsage;
216
+ if (!params.includeRawChunks)
217
+ return;
218
+ }
219
+ controller.enqueue(chunk.type === "finish"
220
+ ? {
221
+ ...chunk,
222
+ usage: withCacheWrites(chunk.usage, rawUsage),
223
+ }
224
+ : chunk);
225
+ },
226
+ })),
227
+ };
228
+ },
229
+ },
230
+ });
231
+ }
@@ -0,0 +1 @@
1
+ export declare function moveOpenAICompatibleCacheControlToMessageContent(body: Record<string, any>): Record<string, any>;
@@ -0,0 +1,71 @@
1
+ export function moveOpenAICompatibleCacheControlToMessageContent(body) {
2
+ if (!Array.isArray(body.messages)) {
3
+ return body;
4
+ }
5
+ return {
6
+ ...body,
7
+ messages: body.messages.map(moveMessageCacheControlToContent),
8
+ };
9
+ }
10
+ function moveMessageCacheControlToContent(message) {
11
+ if (!message ||
12
+ typeof message !== "object" ||
13
+ Array.isArray(message) ||
14
+ !("cache_control" in message)) {
15
+ return message;
16
+ }
17
+ const { cache_control: cacheControl, content, ...messageWithoutCacheControl } = message;
18
+ if (cacheControl == null) {
19
+ return message;
20
+ }
21
+ if (typeof content === "string") {
22
+ return {
23
+ ...messageWithoutCacheControl,
24
+ content: [
25
+ {
26
+ cache_control: cacheControl,
27
+ text: content,
28
+ type: "text",
29
+ },
30
+ ],
31
+ };
32
+ }
33
+ if (!Array.isArray(content)) {
34
+ return message;
35
+ }
36
+ if (content.some(hasCacheControl)) {
37
+ return {
38
+ ...messageWithoutCacheControl,
39
+ content,
40
+ };
41
+ }
42
+ const textPartIndex = findLastTextContentPartIndex(content);
43
+ if (textPartIndex < 0) {
44
+ return message;
45
+ }
46
+ return {
47
+ ...messageWithoutCacheControl,
48
+ content: content.map((part, index) => index === textPartIndex
49
+ ? { ...part, cache_control: cacheControl }
50
+ : part),
51
+ };
52
+ }
53
+ function hasCacheControl(value) {
54
+ return (value != null &&
55
+ typeof value === "object" &&
56
+ !Array.isArray(value) &&
57
+ "cache_control" in value);
58
+ }
59
+ function findLastTextContentPartIndex(content) {
60
+ for (let index = content.length - 1; index >= 0; index -= 1) {
61
+ const part = content[index];
62
+ if (part &&
63
+ typeof part === "object" &&
64
+ !Array.isArray(part) &&
65
+ "type" in part &&
66
+ part.type === "text") {
67
+ return index;
68
+ }
69
+ }
70
+ return -1;
71
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gea-ai/agent-sdk",
3
- "version": "0.1.260916-alpha.2",
3
+ "version": "0.1.260917-alpha.0",
4
4
  "private": false,
5
5
  "homepage": "https://musegea.com/developers",
6
6
  "license": "Apache-2.0",
@@ -88,6 +88,10 @@
88
88
  "./worker-build-plugins": {
89
89
  "types": "./dist/worker-build-plugins.d.ts",
90
90
  "import": "./dist/worker-build-plugins.js"
91
+ },
92
+ "./catalog-language-model": {
93
+ "types": "./dist/catalog-language-model.d.ts",
94
+ "import": "./dist/catalog-language-model.js"
91
95
  }
92
96
  },
93
97
  "publishConfig": {
@@ -100,7 +104,7 @@
100
104
  "@ai-sdk/otel": "1.0.9",
101
105
  "@ai-sdk/provider": "4.0.1",
102
106
  "@ai-sdk/provider-utils": "5.0.2",
103
- "@gea-ai/contract": "0.1.260916-alpha.2",
107
+ "@gea-ai/contract": "0.1.260917-alpha.0",
104
108
  "@opentelemetry/api": "1.9.1",
105
109
  "@opentelemetry/context-async-hooks": "2.10.0",
106
110
  "@opentelemetry/core": "2.10.0",