@opencode-ai/ai 0.0.0-dev-18017 → 0.0.0-dev-18039
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.
- package/dist/protocols/anthropic-messages.d.ts +4 -0
- package/dist/protocols/anthropic-messages.js +6 -8
- package/dist/protocols/open-responses-channel.js +3 -0
- package/dist/protocols/open-responses.js +18 -6
- package/dist/protocols/openai-chat.js +3 -2
- package/dist/protocols/shared.d.ts +2 -0
- package/dist/protocols/shared.js +11 -0
- package/dist/protocols/utils/tool-schema.js +1 -28
- package/dist/providers/anthropic-compatible.d.ts +1 -0
- package/dist/providers/anthropic.d.ts +1 -0
- package/dist/providers/google-vertex-messages.d.ts +1 -0
- package/dist/providers/openrouter.js +3 -2
- package/package.json +3 -3
|
@@ -15,6 +15,7 @@ export type ThinkingInput = {
|
|
|
15
15
|
readonly type: "disabled";
|
|
16
16
|
} | ({
|
|
17
17
|
readonly type: "enabled";
|
|
18
|
+
readonly display?: "summarized" | "omitted";
|
|
18
19
|
} & ({
|
|
19
20
|
readonly budgetTokens: number;
|
|
20
21
|
readonly budget_tokens?: number;
|
|
@@ -194,6 +195,7 @@ export declare const AnthropicMessagesBody: Schema.Struct<{
|
|
|
194
195
|
thinking: Schema.optional<Schema.Union<readonly [Schema.Struct<{
|
|
195
196
|
readonly type: Schema.tag<"enabled">;
|
|
196
197
|
readonly budget_tokens: Schema.Number;
|
|
198
|
+
readonly display: Schema.optional<Schema.Literals<readonly ["summarized", "omitted"]>>;
|
|
197
199
|
}>, Schema.Struct<{
|
|
198
200
|
readonly type: Schema.tag<"adaptive">;
|
|
199
201
|
readonly display: Schema.optional<Schema.Literals<readonly ["summarized", "omitted"]>>;
|
|
@@ -369,6 +371,7 @@ export declare const protocol: Protocol<{
|
|
|
369
371
|
readonly thinking?: {
|
|
370
372
|
readonly type: "enabled";
|
|
371
373
|
readonly budget_tokens: number;
|
|
374
|
+
readonly display?: "summarized" | "omitted" | undefined;
|
|
372
375
|
} | {
|
|
373
376
|
readonly type: "adaptive";
|
|
374
377
|
readonly display?: "summarized" | "omitted" | undefined;
|
|
@@ -611,6 +614,7 @@ export declare const route: Route<{
|
|
|
611
614
|
readonly thinking?: {
|
|
612
615
|
readonly type: "enabled";
|
|
613
616
|
readonly budget_tokens: number;
|
|
617
|
+
readonly display?: "summarized" | "omitted" | undefined;
|
|
614
618
|
} | {
|
|
615
619
|
readonly type: "adaptive";
|
|
616
620
|
readonly display?: "summarized" | "omitted" | undefined;
|
|
@@ -146,6 +146,7 @@ const AnthropicThinking = Schema.Union([
|
|
|
146
146
|
Schema.Struct({
|
|
147
147
|
type: Schema.tag("enabled"),
|
|
148
148
|
budget_tokens: Schema.Number,
|
|
149
|
+
display: Schema.optional(Schema.Literals(["summarized", "omitted"])),
|
|
149
150
|
}),
|
|
150
151
|
Schema.Struct({
|
|
151
152
|
type: Schema.tag("adaptive"),
|
|
@@ -491,14 +492,11 @@ const resolveOptions = Effect.fn("AnthropicMessages.resolveOptions")(function* (
|
|
|
491
492
|
const resolveThinking = Effect.fn("AnthropicMessages.resolveThinking")(function* (input) {
|
|
492
493
|
if (!ProviderShared.isRecord(input))
|
|
493
494
|
return undefined;
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
? "omitted"
|
|
499
|
-
: undefined;
|
|
495
|
+
const display = input.display === "summarized" || input.display === "omitted"
|
|
496
|
+
? input.display
|
|
497
|
+
: undefined;
|
|
498
|
+
if (input.type === "adaptive")
|
|
500
499
|
return { type: "adaptive", ...(display === undefined ? {} : { display }) };
|
|
501
|
-
}
|
|
502
500
|
if (input.type === "disabled")
|
|
503
501
|
return { type: "disabled" };
|
|
504
502
|
if (input.type !== "enabled")
|
|
@@ -510,7 +508,7 @@ const resolveThinking = Effect.fn("AnthropicMessages.resolveThinking")(function*
|
|
|
510
508
|
: undefined;
|
|
511
509
|
if (budget === undefined)
|
|
512
510
|
return yield* ProviderShared.invalidRequest("Anthropic thinking provider option requires budgetTokens");
|
|
513
|
-
return { type: "enabled", budget_tokens: budget };
|
|
511
|
+
return { type: "enabled", budget_tokens: budget, ...(display === undefined ? {} : { display }) };
|
|
514
512
|
});
|
|
515
513
|
const fromRequest = Effect.fn("AnthropicMessages.fromRequest")(function* (request) {
|
|
516
514
|
const generation = request.generation;
|
|
@@ -57,6 +57,9 @@ const driver = (options, body) => {
|
|
|
57
57
|
responseID = created;
|
|
58
58
|
return { type: "frame", frame };
|
|
59
59
|
}
|
|
60
|
+
// Keepalives carry no response state and may arrive before response.created.
|
|
61
|
+
if (event.type === "keepalive")
|
|
62
|
+
return { type: "frame", frame };
|
|
60
63
|
if (!responseID)
|
|
61
64
|
return yield* ProviderShared.eventError(options.id, `${options.name} emitted ${event.type} before response.created`, frame);
|
|
62
65
|
if (event.response?.id && event.response.id !== responseID)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Effect, Schema } from "effect";
|
|
2
2
|
import { HttpTransport } from "../route/transport/index.js";
|
|
3
3
|
import { Protocol } from "../route/protocol.js";
|
|
4
|
-
import { AIError, LLMEvent, Usage, } from "../schema/index.js";
|
|
4
|
+
import { AIError, LLMEvent, ProviderInternalReason, Usage, } from "../schema/index.js";
|
|
5
5
|
import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared.js";
|
|
6
6
|
import { classifyProviderFailure } from "../provider-error.js";
|
|
7
7
|
import { OpenResponsesOptions } from "./utils/open-responses-options.js";
|
|
@@ -234,7 +234,7 @@ export const lowerTool = Effect.fn("OpenResponses.lowerTool")(function* (protoco
|
|
|
234
234
|
type: "function",
|
|
235
235
|
name: tool.name,
|
|
236
236
|
description: tool.description,
|
|
237
|
-
parameters:
|
|
237
|
+
parameters: inputSchema,
|
|
238
238
|
// The common tool definition does not currently express Responses strict-schema policy.
|
|
239
239
|
strict: false,
|
|
240
240
|
};
|
|
@@ -416,8 +416,12 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (reques
|
|
|
416
416
|
const id = itemID(part.providerMetadata, providerMetadataKey);
|
|
417
417
|
if (store !== false && id && !hostedToolReferences.has(id))
|
|
418
418
|
input.push({ type: "item_reference", id });
|
|
419
|
-
if (store === false
|
|
420
|
-
|
|
419
|
+
if (store === false) {
|
|
420
|
+
// The server is not storing this exchange, so the tool outcome has to
|
|
421
|
+
// travel in the input. Non-content results degrade to their text form.
|
|
422
|
+
const content = part.result.type === "content"
|
|
423
|
+
? part.result.value
|
|
424
|
+
: [{ type: "text", text: ProviderShared.toolResultText(part) }];
|
|
421
425
|
input.push({
|
|
422
426
|
role: "user",
|
|
423
427
|
content: yield* Effect.forEach(content, (item) => lowerHostedToolResultContentItem(item, request, extension)),
|
|
@@ -456,6 +460,7 @@ const lowerMessages = Effect.fn("OpenResponses.lowerMessages")(function* (reques
|
|
|
456
460
|
});
|
|
457
461
|
const lowerOptions = (request) => {
|
|
458
462
|
const options = OpenResponsesOptions.resolve(request);
|
|
463
|
+
const cacheKey = ProviderShared.clampPromptCacheKey(request.promptCacheKey);
|
|
459
464
|
return {
|
|
460
465
|
...(options.instructions ? { instructions: options.instructions } : {}),
|
|
461
466
|
...(options.store !== undefined ? { store: options.store } : {}),
|
|
@@ -465,7 +470,7 @@ const lowerOptions = (request) => {
|
|
|
465
470
|
? { stream_options: { include_obfuscation: options.streamOptions.includeObfuscation } }
|
|
466
471
|
: {}),
|
|
467
472
|
...(options.topLogprobs !== undefined ? { top_logprobs: options.topLogprobs } : {}),
|
|
468
|
-
...(
|
|
473
|
+
...(cacheKey ? { prompt_cache_key: cacheKey } : {}),
|
|
469
474
|
...(options.include ? { include: options.include } : {}),
|
|
470
475
|
...(options.reasoningEffort || options.reasoningSummary
|
|
471
476
|
? { reasoning: { effort: options.reasoningEffort, summary: options.reasoningSummary } }
|
|
@@ -856,11 +861,18 @@ export const providerFailure = (id, event, fallback) => {
|
|
|
856
861
|
: typeof event.status_code === "number"
|
|
857
862
|
? event.status_code
|
|
858
863
|
: undefined;
|
|
864
|
+
const reason = event.type === "error" &&
|
|
865
|
+
event.error === undefined &&
|
|
866
|
+
event.response === undefined &&
|
|
867
|
+
summary === undefined &&
|
|
868
|
+
status === undefined
|
|
869
|
+
? new ProviderInternalReason({ message })
|
|
870
|
+
: classifyProviderFailure({ message, code, status, rawBody: body });
|
|
859
871
|
return new AIError({
|
|
860
872
|
module: id,
|
|
861
873
|
method: "stream",
|
|
862
874
|
body,
|
|
863
|
-
reason
|
|
875
|
+
reason,
|
|
864
876
|
});
|
|
865
877
|
};
|
|
866
878
|
const providerError = (state, event, fallback) => providerFailure(state.id, event, fallback);
|
|
@@ -175,7 +175,7 @@ const lowerTool = (tool, inputSchema, options) => ({
|
|
|
175
175
|
function: {
|
|
176
176
|
name: tool.name,
|
|
177
177
|
description: tool.description,
|
|
178
|
-
parameters:
|
|
178
|
+
parameters: inputSchema,
|
|
179
179
|
},
|
|
180
180
|
cache_control: options.cacheControl?.(tool.cache),
|
|
181
181
|
});
|
|
@@ -415,9 +415,10 @@ const hasToolHistory = (messages) => {
|
|
|
415
415
|
};
|
|
416
416
|
const lowerOptions = (request) => {
|
|
417
417
|
const options = OpenAIOptions.resolve(request);
|
|
418
|
+
const cacheKey = ProviderShared.clampPromptCacheKey(request.promptCacheKey);
|
|
418
419
|
return {
|
|
419
420
|
...(options.store !== undefined ? { store: options.store } : {}),
|
|
420
|
-
...(
|
|
421
|
+
...(cacheKey ? { prompt_cache_key: cacheKey } : {}),
|
|
421
422
|
...(options.reasoningEffort ? { reasoning_effort: options.reasoningEffort } : {}),
|
|
422
423
|
};
|
|
423
424
|
};
|
|
@@ -10,6 +10,8 @@ export declare const encodeJson: (input: unknown, options?: import("effect/Schem
|
|
|
10
10
|
export declare const JsonObject: Schema.$Record<Schema.String, Schema.Unknown>;
|
|
11
11
|
export declare const optionalArray: <const S extends Schema.Top>(schema: S) => Schema.optional<Schema.$Array<S>>;
|
|
12
12
|
export declare const optionalNull: <const S extends Schema.Top>(schema: S) => Schema.optional<Schema.NullOr<S>>;
|
|
13
|
+
export declare const OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH = 64;
|
|
14
|
+
export declare const clampPromptCacheKey: (key: string | undefined) => string | undefined;
|
|
13
15
|
/**
|
|
14
16
|
* Streaming tool-call accumulator. Adapters that build a tool call across
|
|
15
17
|
* multiple `tool-input-delta` chunks store the partial JSON input string here
|
package/dist/protocols/shared.js
CHANGED
|
@@ -13,6 +13,17 @@ const isJson = Schema.is(Schema.Json);
|
|
|
13
13
|
export const JsonObject = Schema.Record(Schema.String, Schema.Unknown);
|
|
14
14
|
export const optionalArray = (schema) => Schema.optional(Schema.Array(schema));
|
|
15
15
|
export const optionalNull = (schema) => Schema.optional(Schema.NullOr(schema));
|
|
16
|
+
export const OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH = 64;
|
|
17
|
+
// OpenAI limits `prompt_cache_key` to 64 chars; DeepSeek and Zai inherit the same
|
|
18
|
+
// limit via their OpenAI-compatible APIs. Clamp with unicode-aware slicing.
|
|
19
|
+
export const clampPromptCacheKey = (key) => {
|
|
20
|
+
if (key === undefined)
|
|
21
|
+
return undefined;
|
|
22
|
+
const chars = Array.from(key);
|
|
23
|
+
if (chars.length <= OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH)
|
|
24
|
+
return key;
|
|
25
|
+
return chars.slice(0, OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH).join("");
|
|
26
|
+
};
|
|
16
27
|
/**
|
|
17
28
|
* `Usage.totalTokens` policy shared by every route. Honors a provider-
|
|
18
29
|
* supplied total; otherwise falls back to `inputTokens + outputTokens` only
|
|
@@ -1,20 +1,5 @@
|
|
|
1
1
|
import { isRecord } from "../../utils/record.js";
|
|
2
2
|
import { GeminiToolSchema } from "./gemini-tool-schema.js";
|
|
3
|
-
const removeNullSchemas = (value) => {
|
|
4
|
-
if (Array.isArray(value))
|
|
5
|
-
return value.map(removeNullSchemas);
|
|
6
|
-
if (!isRecord(value))
|
|
7
|
-
return value;
|
|
8
|
-
const fields = Object.fromEntries(Object.entries(value)
|
|
9
|
-
.filter(([key]) => key !== "anyOf")
|
|
10
|
-
.map(([key, field]) => [key, removeNullSchemas(field)]));
|
|
11
|
-
if (!Array.isArray(value.anyOf))
|
|
12
|
-
return fields;
|
|
13
|
-
const variants = value.anyOf.filter((variant) => !isRecord(variant) || variant.type !== "null").map(removeNullSchemas);
|
|
14
|
-
if (variants.length === 1 && isRecord(variants[0]))
|
|
15
|
-
return { ...fields, ...variants[0] };
|
|
16
|
-
return { ...fields, anyOf: variants };
|
|
17
|
-
};
|
|
18
3
|
const tupleItemsSchema = (items) => {
|
|
19
4
|
const projected = items.map(moonshotNode);
|
|
20
5
|
if (projected.length === 0)
|
|
@@ -47,19 +32,7 @@ const moonshot = (schema) => {
|
|
|
47
32
|
const projected = moonshotNode(schema);
|
|
48
33
|
return isRecord(projected) ? projected : {};
|
|
49
34
|
};
|
|
50
|
-
const openAI = (schema) =>
|
|
51
|
-
const variants = Array.isArray(schema.anyOf) ? schema.anyOf.filter(isRecord) : [];
|
|
52
|
-
const flattened = variants.length === 0
|
|
53
|
-
? { ...schema, type: "object" }
|
|
54
|
-
: {
|
|
55
|
-
...Object.fromEntries(Object.entries(schema).filter(([key]) => key !== "anyOf")),
|
|
56
|
-
type: "object",
|
|
57
|
-
properties: variants.reduce((properties, variant) => ({ ...(isRecord(variant.properties) ? variant.properties : {}), ...properties }), {}),
|
|
58
|
-
additionalProperties: false,
|
|
59
|
-
};
|
|
60
|
-
const normalized = removeNullSchemas(flattened);
|
|
61
|
-
return isRecord(normalized) ? normalized : { type: "object" };
|
|
62
|
-
};
|
|
35
|
+
const openAI = (schema) => schema;
|
|
63
36
|
const responses = openAI;
|
|
64
37
|
const gemini = (schema) => GeminiToolSchema.convert(schema) ?? {};
|
|
65
38
|
const modelCompatibility = (schema, compatibility) => {
|
|
@@ -182,6 +182,7 @@ export declare const routes: import("../route/client.js").Route<{
|
|
|
182
182
|
readonly thinking?: {
|
|
183
183
|
readonly type: "enabled";
|
|
184
184
|
readonly budget_tokens: number;
|
|
185
|
+
readonly display?: "summarized" | "omitted" | undefined;
|
|
185
186
|
} | {
|
|
186
187
|
readonly type: "adaptive";
|
|
187
188
|
readonly display?: "summarized" | "omitted" | undefined;
|
|
@@ -166,6 +166,7 @@ export declare const routes: import("../route/client.js").Route<{
|
|
|
166
166
|
readonly thinking?: {
|
|
167
167
|
readonly type: "enabled";
|
|
168
168
|
readonly budget_tokens: number;
|
|
169
|
+
readonly display?: "summarized" | "omitted" | undefined;
|
|
169
170
|
} | {
|
|
170
171
|
readonly type: "adaptive";
|
|
171
172
|
readonly display?: "summarized" | "omitted" | undefined;
|
|
@@ -180,6 +180,7 @@ export declare const routes: Route<{
|
|
|
180
180
|
thinking?: {
|
|
181
181
|
readonly type: "enabled";
|
|
182
182
|
readonly budget_tokens: number;
|
|
183
|
+
readonly display?: "summarized" | "omitted" | undefined;
|
|
183
184
|
} | {
|
|
184
185
|
readonly type: "adaptive";
|
|
185
186
|
readonly display?: "summarized" | "omitted" | undefined;
|
|
@@ -8,7 +8,7 @@ import { ProviderID } from "../schema/index.js";
|
|
|
8
8
|
import * as OpenAICompatibleProfiles from "./openai-compatible-profile.js";
|
|
9
9
|
import * as OpenAIChat from "../protocols/openai-chat.js";
|
|
10
10
|
import { newBreakpoints, ttlBucket } from "../protocols/utils/cache.js";
|
|
11
|
-
import { isRecord } from "../protocols/shared.js";
|
|
11
|
+
import { isRecord, ProviderShared } from "../protocols/shared.js";
|
|
12
12
|
export const profile = OpenAICompatibleProfiles.profiles.openrouter;
|
|
13
13
|
export const id = ProviderID.make(profile.provider);
|
|
14
14
|
const ADAPTER = "openrouter";
|
|
@@ -39,11 +39,12 @@ export const protocol = Protocol.make({
|
|
|
39
39
|
reasoning_details: reasoningDetails,
|
|
40
40
|
};
|
|
41
41
|
});
|
|
42
|
+
const cacheKey = ProviderShared.clampPromptCacheKey(request.promptCacheKey);
|
|
42
43
|
return {
|
|
43
44
|
...body,
|
|
44
45
|
messages,
|
|
45
46
|
...bodyOptions(request.providerOptions),
|
|
46
|
-
...(
|
|
47
|
+
...(cacheKey ? { prompt_cache_key: cacheKey } : {}),
|
|
47
48
|
};
|
|
48
49
|
})),
|
|
49
50
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
|
-
"version": "0.0.0-dev-
|
|
3
|
+
"version": "0.0.0-dev-18039",
|
|
4
4
|
"name": "@opencode-ai/ai",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@clack/prompts": "1.0.0-alpha.1",
|
|
32
32
|
"@effect/platform-node": "4.0.0-rc.111",
|
|
33
|
-
"@opencode-ai/http-recorder": "0.0.0-dev-
|
|
33
|
+
"@opencode-ai/http-recorder": "0.0.0-dev-18039",
|
|
34
34
|
"@tsconfig/bun": "1.0.9",
|
|
35
35
|
"@types/bun": "1.3.13",
|
|
36
36
|
"@typescript/native-preview": "7.0.0-dev.20251207.1",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@smithy/eventstream-codec": "4.2.14",
|
|
41
41
|
"@smithy/util-utf8": "4.2.2",
|
|
42
|
-
"@opencode-ai/schema": "0.0.0-dev-
|
|
42
|
+
"@opencode-ai/schema": "0.0.0-dev-18039",
|
|
43
43
|
"aws4fetch": "1.0.20",
|
|
44
44
|
"effect": "4.0.0-rc.111",
|
|
45
45
|
"google-auth-library": "10.5.0"
|