@opencode-ai/ai
Advanced tools
+2
-0
@@ -48,2 +48,3 @@ import { Effect, JsonSchema, Schema } from "effect"; | ||
| readonly type: "text-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -74,2 +75,3 @@ readonly [x: string]: { | ||
| readonly type: "reasoning-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -76,0 +78,0 @@ readonly [x: string]: { |
@@ -832,2 +832,3 @@ import { Effect, Schema } from "effect"; | ||
| readonly type: "text-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -858,2 +859,3 @@ readonly [x: string]: { | ||
| readonly type: "reasoning-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -860,0 +862,0 @@ readonly [x: string]: { |
@@ -13,4 +13,8 @@ import { LLMEvent, type FinishReasonDetails, type ProviderMetadata, type Usage } from "../../schema/index.js"; | ||
| export declare const reasoningDelta: (state: State, events: LLMEvent[], id: string, text: string, providerMetadata?: ProviderMetadata) => State; | ||
| export declare const reasoningEnd: (state: State, events: LLMEvent[], id: string, providerMetadata?: ProviderMetadata) => State; | ||
| export declare const textEnd: (state: State, events: LLMEvent[], id: string, providerMetadata?: ProviderMetadata) => State; | ||
| export declare const reasoningEnd: (state: State, events: LLMEvent[], id: string, providerMetadata?: ProviderMetadata, | ||
| /** Authoritative complete value; replaces accumulated deltas when present. */ | ||
| text?: string) => State; | ||
| export declare const textEnd: (state: State, events: LLMEvent[], id: string, providerMetadata?: ProviderMetadata, | ||
| /** Authoritative complete value; replaces accumulated deltas when present. */ | ||
| text?: string) => State; | ||
| export declare const finish: (state: State, events: LLMEvent[], input: { | ||
@@ -17,0 +21,0 @@ readonly reason: FinishReasonDetails; |
@@ -33,7 +33,9 @@ import { LLMEvent } from "../../schema/index.js"; | ||
| }; | ||
| export const reasoningEnd = (state, events, id, providerMetadata) => { | ||
| export const reasoningEnd = (state, events, id, providerMetadata, | ||
| /** Authoritative complete value; replaces accumulated deltas when present. */ | ||
| text) => { | ||
| if (!state.reasoning.has(id)) | ||
| return state; | ||
| const stepped = stepStart(state, events); | ||
| events.push(LLMEvent.reasoningEnd({ id, providerMetadata })); | ||
| events.push(LLMEvent.reasoningEnd({ id, text, providerMetadata })); | ||
| const reasoning = new Set(stepped.reasoning); | ||
@@ -43,10 +45,12 @@ reasoning.delete(id); | ||
| }; | ||
| export const textEnd = (state, events, id, providerMetadata) => { | ||
| export const textEnd = (state, events, id, providerMetadata, | ||
| /** Authoritative complete value; replaces accumulated deltas when present. */ | ||
| text) => { | ||
| if (!state.text.has(id)) | ||
| return state; | ||
| const stepped = stepStart(state, events); | ||
| events.push(LLMEvent.textEnd({ id, providerMetadata })); | ||
| const text = new Set(stepped.text); | ||
| text.delete(id); | ||
| return { ...stepped, text }; | ||
| events.push(LLMEvent.textEnd({ id, text, providerMetadata })); | ||
| const open = new Set(stepped.text); | ||
| open.delete(id); | ||
| return { ...stepped, text: open }; | ||
| }; | ||
@@ -53,0 +57,0 @@ const closeOpenBlocks = (state, events) => { |
@@ -96,2 +96,3 @@ import { Effect } from "effect"; | ||
| readonly type: "text-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -122,2 +123,3 @@ readonly [x: string]: { | ||
| readonly type: "reasoning-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -292,2 +294,3 @@ readonly [x: string]: { | ||
| readonly type: "text-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -318,2 +321,3 @@ readonly [x: string]: { | ||
| readonly type: "reasoning-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -485,2 +489,3 @@ readonly [x: string]: { | ||
| readonly type: "text-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -511,2 +516,3 @@ readonly [x: string]: { | ||
| readonly type: "reasoning-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -513,0 +519,0 @@ readonly [x: string]: { |
+19
-34
@@ -27,2 +27,3 @@ import { Option, Schema } from "effect"; | ||
| /model_context_window_exceeded/i, | ||
| /range of input length should be/i, | ||
| /too many tokens/i, | ||
@@ -42,2 +43,3 @@ /token limit exceeded/i, | ||
| const QUOTA_CODES = new Set(["insufficient_quota", "usage_not_included", "billing_error"]); | ||
| const AUTH_CODES = new Set(["authentication_error", "permission_error"]); | ||
| const SERVER_CODES = new Set([ | ||
@@ -58,5 +60,8 @@ "api_error", | ||
| const CONTENT_POLICY_TEXT = /content[-_\s]?policy|content_filter|safety/i; | ||
| const NETWORK_ERROR_TEXT = /network[-_\s]error/i; | ||
| // Keep HTTP failures and provider-reported stream failures on one typed path so | ||
| // session retry policy never needs provider-specific string matching. | ||
| // Classification records affirmative evidence about a failure. Deterministic | ||
| // failures need positive identification (a 4xx status, quota/auth/policy | ||
| // signals); anything unrecognized stays UnknownProvider, which the session | ||
| // retry policy treats as retry-eligible because transient failures arrive in | ||
| // unpredictable shapes while deterministic rejections almost always carry a | ||
| // status or known code. | ||
| export function classifyProviderFailure(input) { | ||
@@ -82,11 +87,7 @@ const details = { message: input.message, body: input.rawBody, http: input.http, cause: input.cause }; | ||
| return new QuotaExceededError(details); | ||
| if (input.status === 401) | ||
| return new AuthenticationError({ ...details, kind: "invalid" }); | ||
| if (input.status === 403) | ||
| return new AuthenticationError({ ...details, kind: "insufficient-permissions" }); | ||
| if (codes.includes("authentication_error")) | ||
| return new AuthenticationError({ ...details, kind: "invalid" }); | ||
| if (codes.includes("permission_error")) | ||
| return new AuthenticationError({ ...details, kind: "insufficient-permissions" }); | ||
| if (codes.some((code) => code.includes("rate_limit") || code === "too_many_requests" || code === "throttlingexception")) | ||
| if (input.status === 401 || input.status === 403 || codes.some((code) => AUTH_CODES.has(code))) | ||
| return new AuthenticationError(details); | ||
| if (input.status === 429 || | ||
| codes.some((code) => code.includes("rate_limit") || code === "too_many_requests" || code === "throttlingexception") || | ||
| RATE_LIMIT_TEXT.test(text)) | ||
| return new RateLimitError({ | ||
@@ -97,11 +98,6 @@ ...details, | ||
| }); | ||
| if (RATE_LIMIT_TEXT.test(text)) | ||
| return new RateLimitError({ | ||
| ...details, | ||
| retryAfterMs: input.retryAfterMs, | ||
| rateLimit: input.rateLimit, | ||
| }); | ||
| if (NETWORK_ERROR_TEXT.test(text)) | ||
| return new ProviderInternalError(details); | ||
| if (codes.some((code) => SERVER_CODES.has(code) || code.includes("exhausted") || code.includes("unavailable"))) | ||
| if (input.status === 408 || | ||
| input.status === 409 || | ||
| (input.status !== undefined && input.status >= 500) || | ||
| codes.some((code) => SERVER_CODES.has(code) || code.includes("exhausted") || code.includes("unavailable"))) | ||
| return new ProviderInternalError({ | ||
@@ -111,17 +107,6 @@ ...details, | ||
| }); | ||
| if (input.status === 429) { | ||
| return new RateLimitError({ | ||
| ...details, | ||
| retryAfterMs: input.retryAfterMs, | ||
| rateLimit: input.rateLimit, | ||
| }); | ||
| } | ||
| if (input.status === 408 || input.status === 409 || (input.status !== undefined && input.status >= 500)) | ||
| return new ProviderInternalError({ | ||
| ...details, | ||
| retryAfterMs: input.retryAfterMs, | ||
| }); | ||
| if (codes.some((code) => INVALID_REQUEST_CODES.has(code))) | ||
| return new InvalidRequestError(details); | ||
| if (input.status === 400 || input.status === 404 || input.status === 413 || input.status === 422) | ||
| // Any remaining 4xx is a deterministic rejection of this request. | ||
| if (input.status !== undefined && input.status >= 400 && input.status < 500) | ||
| return new InvalidRequestError(details); | ||
@@ -128,0 +113,0 @@ return new UnknownProviderError(details); |
@@ -80,3 +80,3 @@ import { Config, Effect, Redacted } from "effect"; | ||
| reason: error instanceof MissingCredentialError | ||
| ? new AuthenticationError({ message: error.message, cause: error, kind: "missing" }) | ||
| ? new AuthenticationError({ message: error.message, cause: error }) | ||
| : new InvalidRequestError({ message: `Failed to resolve auth config: ${error.message}`, cause: error }), | ||
@@ -83,0 +83,0 @@ }); |
@@ -176,2 +176,3 @@ import { Context, Effect, Layer, Schema, Stream } from "effect"; | ||
| readonly type: "text-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -202,2 +203,3 @@ readonly [x: string]: { | ||
| readonly type: "reasoning-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -204,0 +206,0 @@ readonly [x: string]: { |
@@ -42,7 +42,6 @@ import { Schema } from "effect"; | ||
| declare const AuthenticationError_base: Schema.Class<AuthenticationError, Schema.TaggedStruct<"Authentication", { | ||
| readonly kind: Schema.Literals<readonly ["missing", "invalid", "expired", "insufficient-permissions", "unknown"]>; | ||
| readonly message: Schema.String; | ||
| readonly body: Schema.optional<Schema.String>; | ||
| readonly http: Schema.optional<typeof HttpContext>; | ||
| readonly cause: Schema.optional<Schema.Defect>; | ||
| message: Schema.String; | ||
| body: Schema.optional<Schema.String>; | ||
| http: Schema.optional<typeof HttpContext>; | ||
| cause: Schema.optional<Schema.Defect>; | ||
| }>, import("effect/Cause").YieldableError>; | ||
@@ -49,0 +48,0 @@ export declare class AuthenticationError extends AuthenticationError_base { |
@@ -38,6 +38,3 @@ import { Schema } from "effect"; | ||
| } | ||
| export class AuthenticationError extends Schema.TaggedError("AI.Error.Authentication")("Authentication", { | ||
| ...ReasonFields, | ||
| kind: Schema.Literals(["missing", "invalid", "expired", "insufficient-permissions", "unknown"]), | ||
| }) { | ||
| export class AuthenticationError extends Schema.TaggedError("AI.Error.Authentication")("Authentication", ReasonFields) { | ||
| } | ||
@@ -44,0 +41,0 @@ export class RateLimitError extends Schema.TaggedError("AI.Error.RateLimit")("RateLimit", { |
+32
-14
@@ -94,2 +94,4 @@ import { Schema } from "effect"; | ||
| id: ContentBlockID, | ||
| /** Authoritative complete value; replaces accumulated deltas when present. */ | ||
| text: Schema.optional(Schema.String), | ||
| providerMetadata: Schema.optional(ProviderMetadata), | ||
@@ -111,2 +113,4 @@ }).annotate({ identifier: "LLM.Event.TextEnd" }); | ||
| id: ContentBlockID, | ||
| /** Authoritative complete value; replaces accumulated deltas when present. */ | ||
| text: Schema.optional(Schema.String), | ||
| providerMetadata: Schema.optional(ProviderMetadata), | ||
@@ -264,10 +268,22 @@ }).annotate({ identifier: "LLM.Event.ReasoningEnd" }); | ||
| }); | ||
| const responseText = (events) => events | ||
| .filter(LLMEvent.is.textDelta) | ||
| .map((event) => event.text) | ||
| .join(""); | ||
| const responseReasoning = (events) => events | ||
| .filter(LLMEvent.is.reasoningDelta) | ||
| .map((event) => event.text) | ||
| .join(""); | ||
| /** Joins deltas per fragment, letting an authoritative end value replace that fragment's accumulated deltas. */ | ||
| const joinFragments = (events, isDelta, isEnd) => { | ||
| const order = []; | ||
| const parts = new Map(); | ||
| for (const event of events) { | ||
| if (isDelta(event)) { | ||
| if (!parts.has(event.id)) | ||
| order.push(event.id); | ||
| parts.set(event.id, (parts.get(event.id) ?? "") + event.text); | ||
| } | ||
| if (isEnd(event) && event.text !== undefined) { | ||
| if (!parts.has(event.id)) | ||
| order.push(event.id); | ||
| parts.set(event.id, event.text); | ||
| } | ||
| } | ||
| return order.map((id) => parts.get(id)).join(""); | ||
| }; | ||
| const responseText = (events) => joinFragments(events, LLMEvent.is.textDelta, LLMEvent.is.textEnd); | ||
| const responseReasoning = (events) => joinFragments(events, LLMEvent.is.reasoningDelta, LLMEvent.is.reasoningEnd); | ||
| const responseUsage = (events) => events.reduce((usage, event) => ("usage" in event && event.usage !== undefined ? event.usage : usage), undefined); | ||
@@ -339,6 +355,7 @@ const emptyResponseState = () => ({ | ||
| return state; | ||
| const text = event.text ?? current.text; | ||
| const providerMetadata = event.providerMetadata ?? current.providerMetadata; | ||
| return { | ||
| ...replaceContent(state, current.contentIndex, textContent(current.text, providerMetadata)), | ||
| textParts: { ...state.textParts, [event.id]: { ...current, providerMetadata } }, | ||
| ...replaceContent(state, current.contentIndex, textContent(text, providerMetadata)), | ||
| textParts: { ...state.textParts, [event.id]: { ...current, text, providerMetadata } }, | ||
| }; | ||
@@ -373,6 +390,7 @@ }; | ||
| return state; | ||
| const text = event.text ?? current.text; | ||
| const providerMetadata = event.providerMetadata ?? current.providerMetadata; | ||
| return { | ||
| ...replaceContent(state, current.contentIndex, reasoningContent(current.text, providerMetadata)), | ||
| reasoningParts: { ...state.reasoningParts, [event.id]: { ...current, providerMetadata } }, | ||
| ...replaceContent(state, current.contentIndex, reasoningContent(text, providerMetadata)), | ||
| reasoningParts: { ...state.reasoningParts, [event.id]: { ...current, text, providerMetadata } }, | ||
| }; | ||
@@ -465,7 +483,7 @@ }; | ||
| }) { | ||
| /** Concatenated assistant text assembled from streamed `text-delta` events. */ | ||
| /** Concatenated assistant text; each fragment's `text-end` value replaces its accumulated deltas when present. */ | ||
| get text() { | ||
| return responseText(this.events); | ||
| } | ||
| /** Concatenated reasoning text assembled from streamed `reasoning-delta` events. */ | ||
| /** Concatenated reasoning text; each fragment's `reasoning-end` value replaces its accumulated deltas when present. */ | ||
| get reasoning() { | ||
@@ -472,0 +490,0 @@ return responseReasoning(this.events); |
+16
-0
@@ -53,2 +53,3 @@ export * as TestLLM from "./testing.js"; | ||
| readonly type: "text-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -79,2 +80,3 @@ readonly [x: string]: { | ||
| readonly type: "reasoning-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -238,2 +240,3 @@ readonly [x: string]: { | ||
| readonly type: "text-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -264,2 +267,3 @@ readonly [x: string]: { | ||
| readonly type: "reasoning-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -423,2 +427,3 @@ readonly [x: string]: { | ||
| readonly type: "text-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -449,2 +454,3 @@ readonly [x: string]: { | ||
| readonly type: "reasoning-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -608,2 +614,3 @@ readonly [x: string]: { | ||
| readonly type: "text-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -634,2 +641,3 @@ readonly [x: string]: { | ||
| readonly type: "reasoning-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -793,2 +801,3 @@ readonly [x: string]: { | ||
| readonly type: "text-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -819,2 +828,3 @@ readonly [x: string]: { | ||
| readonly type: "reasoning-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -978,2 +988,3 @@ readonly [x: string]: { | ||
| readonly type: "text-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -1004,2 +1015,3 @@ readonly [x: string]: { | ||
| readonly type: "reasoning-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -1163,2 +1175,3 @@ readonly [x: string]: { | ||
| readonly type: "text-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -1189,2 +1202,3 @@ readonly [x: string]: { | ||
| readonly type: "reasoning-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -1348,2 +1362,3 @@ readonly [x: string]: { | ||
| readonly type: "text-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -1374,2 +1389,3 @@ readonly [x: string]: { | ||
| readonly type: "reasoning-end"; | ||
| readonly text?: string | undefined; | ||
| readonly providerMetadata?: { | ||
@@ -1376,0 +1392,0 @@ readonly [x: string]: { |
+3
-3
| { | ||
| "$schema": "https://json.schemastore.org/package.json", | ||
| "version": "0.0.0-dev-18545", | ||
| "version": "0.0.0-dev-18548", | ||
| "name": "@opencode-ai/ai", | ||
@@ -33,3 +33,3 @@ "type": "module", | ||
| "@effect/platform-node": "4.0.0-rc.112", | ||
| "@opencode-ai/http-recorder": "0.0.0-dev-18545", | ||
| "@opencode-ai/http-recorder": "0.0.0-dev-18548", | ||
| "@tsconfig/bun": "1.0.9", | ||
@@ -43,3 +43,3 @@ "@types/bun": "1.3.13", | ||
| "@smithy/util-utf8": "4.2.2", | ||
| "@opencode-ai/schema": "0.0.0-dev-18545", | ||
| "@opencode-ai/schema": "0.0.0-dev-18548", | ||
| "aws4fetch": "1.0.20", | ||
@@ -46,0 +46,0 @@ "effect": "4.0.0-rc.112", |
Sorry, the diff of this file is too big to display
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
1475140
0.45%34018
0.35%+ Added
- Removed