@xandreed/plugin-models 0.13.0 → 0.15.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.
- package/README.md +1 -1
- package/dist/auth/anthropicOAuth.d.ts +45 -11
- package/dist/auth/anthropicOAuth.js +23 -23
- package/dist/auth/auth-store.conformance.d.ts +63 -0
- package/dist/auth/auth-store.conformance.js +152 -0
- package/dist/auth/credential-input.entity.d.ts +52 -0
- package/dist/auth/credential-input.entity.functions.d.ts +18 -0
- package/dist/auth/credential-input.entity.functions.js +12 -0
- package/dist/auth/credential-input.entity.js +22 -0
- package/dist/auth/credential-refresh.entity.d.ts +17 -0
- package/dist/auth/credential-refresh.entity.functions.d.ts +57 -0
- package/dist/auth/credential-refresh.entity.functions.js +21 -0
- package/dist/auth/credential-refresh.entity.js +2 -0
- package/dist/auth/localAuth.d.ts +12 -2
- package/dist/auth/localAuth.js +58 -86
- package/dist/auth/memory-auth.adapter.d.ts +35 -0
- package/dist/auth/memory-auth.adapter.js +35 -0
- package/dist/auth/openAiCodexOAuth.d.ts +28 -6
- package/dist/auth/openAiCodexOAuth.js +22 -32
- package/dist/auth/token-response.entity.d.ts +21 -0
- package/dist/auth/token-response.entity.functions.d.ts +20 -0
- package/dist/auth/token-response.entity.functions.js +10 -0
- package/dist/auth/token-response.entity.js +10 -0
- package/dist/compat/index.d.ts +2 -0
- package/dist/compat/index.js +1 -0
- package/dist/conformance/index.d.ts +6 -0
- package/dist/conformance/index.js +3 -0
- package/dist/index.d.ts +30 -13
- package/dist/index.js +25 -50
- package/dist/llm/chat-wire.entity.d.ts +123 -0
- package/dist/llm/chat-wire.entity.functions.d.ts +28 -0
- package/dist/llm/chat-wire.entity.functions.js +10 -0
- package/dist/llm/chat-wire.entity.js +37 -0
- package/dist/llm/compat-telemetry.adapter.d.ts +6 -0
- package/dist/llm/compat-telemetry.adapter.js +21 -0
- package/dist/llm/compat.d.ts +84 -33
- package/dist/llm/compat.js +188 -121
- package/dist/llm/model-completion.adapter.d.ts +49 -0
- package/dist/llm/model-completion.adapter.js +77 -0
- package/dist/llm/model-completion.entity.d.ts +4 -0
- package/dist/llm/model-completion.entity.functions.d.ts +3 -0
- package/dist/llm/model-completion.entity.functions.js +7 -0
- package/dist/llm/model-completion.entity.js +7 -0
- package/dist/llm/model-retry-policy.entity.d.ts +37 -0
- package/dist/llm/model-retry-policy.entity.functions.d.ts +16 -0
- package/dist/llm/model-retry-policy.entity.functions.js +33 -0
- package/dist/llm/model-retry-policy.entity.js +23 -0
- package/dist/llm/model-settings.entity.d.ts +85 -0
- package/dist/llm/model-settings.entity.functions.d.ts +11 -0
- package/dist/llm/model-settings.entity.functions.js +49 -0
- package/dist/llm/model-settings.entity.js +28 -0
- package/dist/llm/model-usage.entity.d.ts +61 -0
- package/dist/llm/model-usage.entity.functions.d.ts +11 -0
- package/dist/llm/model-usage.entity.functions.js +24 -0
- package/dist/llm/model-usage.entity.js +19 -0
- package/dist/llm/modelCatalog.d.ts +47 -7
- package/dist/llm/modelCatalog.js +31 -57
- package/dist/llm/openAiCodex.d.ts +18 -7
- package/dist/llm/openAiCodex.js +57 -45
- package/dist/llm/openAiCodexWebSocket.d.ts +60 -7
- package/dist/llm/openAiCodexWebSocket.js +113 -105
- package/dist/llm/prompt-cache-frame.entity.d.ts +18 -0
- package/dist/llm/prompt-cache-frame.entity.js +10 -0
- package/dist/llm/prompt-cache.entity.d.ts +57 -0
- package/dist/llm/prompt-cache.entity.functions.d.ts +8 -0
- package/dist/llm/prompt-cache.entity.functions.js +73 -0
- package/dist/llm/prompt-cache.entity.js +27 -0
- package/dist/llm/provider-client.adapter.d.ts +9 -0
- package/dist/llm/provider-client.adapter.js +60 -0
- package/dist/llm/provider-failure.entity.d.ts +42 -0
- package/dist/llm/provider-failure.entity.functions.d.ts +8 -0
- package/dist/llm/provider-failure.entity.functions.js +67 -0
- package/dist/llm/provider-failure.entity.js +20 -0
- package/dist/llm/provider-profile.entity.d.ts +79 -0
- package/dist/llm/provider-profile.entity.functions.d.ts +23 -0
- package/dist/llm/provider-profile.entity.functions.js +83 -0
- package/dist/llm/provider-profile.entity.js +10 -0
- package/dist/llm/providers.d.ts +125 -29
- package/dist/llm/providers.js +311 -117
- package/dist/llm/retry.d.ts +11 -47
- package/dist/llm/retry.js +33 -52
- package/dist/llm/router.d.ts +56 -28
- package/dist/llm/router.js +125 -88
- package/dist/llm/sse.d.ts +58 -29
- package/dist/llm/sse.js +47 -39
- package/dist/llm/thinking-binding.entity.d.ts +49 -0
- package/dist/llm/thinking-binding.entity.functions.d.ts +21 -0
- package/dist/llm/thinking-binding.entity.functions.js +16 -0
- package/dist/llm/thinking-binding.entity.js +11 -0
- package/dist/llm/utilityLlm.d.ts +14 -5
- package/dist/llm/utilityLlm.js +18 -13
- package/dist/plugin.adapter.d.ts +154 -6
- package/dist/plugin.adapter.js +53 -10
- package/dist/ports/auth-store.port.d.ts +73 -0
- package/dist/ports/auth-store.port.js +24 -0
- package/dist/ports/host-credentials.port.d.ts +22 -0
- package/dist/ports/host-credentials.port.js +4 -0
- package/dist/ports/model-call-completion.port.d.ts +9 -0
- package/dist/ports/model-call-completion.port.js +2 -0
- package/dist/ports/model-endpoints.port.d.ts +66 -19
- package/dist/ports/model-endpoints.port.js +22 -4
- package/dist/ports/model-retry-policy.port.d.ts +17 -0
- package/dist/ports/model-retry-policy.port.js +3 -0
- package/dist/ports/model-settings.port.d.ts +32 -0
- package/dist/ports/model-settings.port.js +4 -0
- package/dist/ports/models-transport.port.d.ts +53 -0
- package/dist/ports/models-transport.port.js +7 -0
- package/dist/ports/prompt-cache-frame.port.d.ts +21 -0
- package/dist/ports/prompt-cache-frame.port.js +4 -0
- package/dist/ports/settings-store.port.d.ts +70 -0
- package/dist/ports/settings-store.port.js +33 -0
- package/dist/ports/thinking-binding.port.d.ts +7 -0
- package/dist/ports/thinking-binding.port.js +4 -0
- package/dist/settings/localSettings.d.ts +2 -1
- package/dist/settings/localSettings.js +18 -61
- package/dist/settings/memory-settings.adapter.d.ts +12 -0
- package/dist/settings/memory-settings.adapter.js +11 -0
- package/dist/settings/roleView.d.ts +3 -1
- package/dist/settings/roleView.js +1 -1
- package/dist/settings/settings-store.conformance.d.ts +47 -0
- package/dist/settings/settings-store.conformance.js +100 -0
- package/dist/settings/settings-value.entity.d.ts +15 -0
- package/dist/settings/settings-value.entity.functions.d.ts +51 -0
- package/dist/settings/settings-value.entity.functions.js +42 -0
- package/dist/settings/settings-value.entity.js +2 -0
- package/dist/storage/json-file.d.ts +18 -0
- package/dist/storage/json-file.js +26 -0
- package/dist/transport/http-proxy.entity.d.ts +104 -0
- package/dist/transport/http-proxy.entity.functions.d.ts +51 -0
- package/dist/transport/http-proxy.entity.functions.js +112 -0
- package/dist/transport/http-proxy.entity.js +42 -0
- package/dist/transport/model-request.conformance.d.ts +13 -0
- package/dist/transport/model-request.conformance.js +36 -0
- package/dist/transport/models-transport.adapter.d.ts +49 -0
- package/dist/transport/models-transport.adapter.js +99 -0
- package/dist/transport/resilient-http-client.adapter.d.ts +73 -0
- package/dist/transport/resilient-http-client.adapter.js +177 -0
- package/dist/transport/undici-fetch.adapter.d.ts +13 -0
- package/dist/transport/undici-fetch.adapter.js +1 -0
- package/package.json +13 -6
- package/dist/auth/anthropicOAuth.test.d.ts +0 -1
- package/dist/auth/localAuth.test.d.ts +0 -1
- package/dist/llm/compat.test.d.ts +0 -1
- package/dist/llm/modelCatalog.test.d.ts +0 -1
- package/dist/llm/providers.test.d.ts +0 -1
- package/dist/llm/retry.test.d.ts +0 -1
- package/dist/llm/router.test.d.ts +0 -1
- package/dist/llm/sse.test.d.ts +0 -1
- package/dist/plugin.adapter.test.d.ts +0 -1
- package/dist/settings/localSettings.test.d.ts +0 -1
- package/dist/settings/roleView.test.d.ts +0 -1
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
/** The usage object OpenAI-compatible completions carry (streaming sends it
|
|
3
|
+
* on the last chunk under `stream_options: {include_usage: true}`). */
|
|
4
|
+
export declare const CompletionUsage: Schema.Struct<{
|
|
5
|
+
readonly prompt_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
6
|
+
readonly completion_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
7
|
+
readonly total_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
8
|
+
/** DeepSeek-style cache accounting. */
|
|
9
|
+
readonly prompt_cache_hit_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
10
|
+
/** Moonshot-style cache accounting. */
|
|
11
|
+
readonly cached_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
12
|
+
/** OpenAI-native cache reads; gateways (OpenCode's among them) add the cache writes. */
|
|
13
|
+
readonly prompt_tokens_details: Schema.optionalKey<Schema.NullOr<Schema.Struct<{
|
|
14
|
+
readonly cached_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
15
|
+
readonly cache_write_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
16
|
+
}>>>;
|
|
17
|
+
/** OpenAI-native reasoning accounting, counted inside `completion_tokens`. */
|
|
18
|
+
readonly completion_tokens_details: Schema.optionalKey<Schema.NullOr<Schema.Struct<{
|
|
19
|
+
readonly reasoning_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
20
|
+
}>>>;
|
|
21
|
+
}>;
|
|
22
|
+
export type CompletionUsage = typeof CompletionUsage.Type;
|
|
23
|
+
/** One non-streaming chat completion. */
|
|
24
|
+
export declare const ChatCompletion: Schema.Struct<{
|
|
25
|
+
readonly choices: Schema.optionalKey<Schema.NullOr<Schema.$Array<Schema.Struct<{
|
|
26
|
+
readonly finish_reason: Schema.optionalKey<Schema.NullOr<Schema.String>>;
|
|
27
|
+
readonly message: Schema.optionalKey<Schema.NullOr<Schema.Struct<{
|
|
28
|
+
readonly tool_calls: Schema.optionalKey<Schema.NullOr<Schema.$Array<Schema.Struct<{
|
|
29
|
+
readonly id: Schema.optionalKey<Schema.NullOr<Schema.brand<Schema.String, "ToolCallId">>>;
|
|
30
|
+
readonly function: Schema.optionalKey<Schema.NullOr<Schema.Struct<{
|
|
31
|
+
readonly name: Schema.optionalKey<Schema.NullOr<Schema.String>>;
|
|
32
|
+
readonly arguments: Schema.optionalKey<Schema.NullOr<Schema.String>>;
|
|
33
|
+
}>>>;
|
|
34
|
+
}>>>>;
|
|
35
|
+
readonly content: Schema.optionalKey<Schema.NullOr<Schema.String>>;
|
|
36
|
+
readonly reasoning_content: Schema.optionalKey<Schema.NullOr<Schema.String>>;
|
|
37
|
+
readonly reasoning: Schema.optionalKey<Schema.NullOr<Schema.String>>;
|
|
38
|
+
}>>>;
|
|
39
|
+
}>>>>;
|
|
40
|
+
readonly usage: Schema.optionalKey<Schema.NullOr<Schema.Struct<{
|
|
41
|
+
readonly prompt_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
42
|
+
readonly completion_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
43
|
+
readonly total_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
44
|
+
/** DeepSeek-style cache accounting. */
|
|
45
|
+
readonly prompt_cache_hit_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
46
|
+
/** Moonshot-style cache accounting. */
|
|
47
|
+
readonly cached_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
48
|
+
/** OpenAI-native cache reads; gateways (OpenCode's among them) add the cache writes. */
|
|
49
|
+
readonly prompt_tokens_details: Schema.optionalKey<Schema.NullOr<Schema.Struct<{
|
|
50
|
+
readonly cached_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
51
|
+
readonly cache_write_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
52
|
+
}>>>;
|
|
53
|
+
/** OpenAI-native reasoning accounting, counted inside `completion_tokens`. */
|
|
54
|
+
readonly completion_tokens_details: Schema.optionalKey<Schema.NullOr<Schema.Struct<{
|
|
55
|
+
readonly reasoning_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
56
|
+
}>>>;
|
|
57
|
+
}>>>;
|
|
58
|
+
}>;
|
|
59
|
+
export type ChatCompletion = typeof ChatCompletion.Type;
|
|
60
|
+
/** A streamed tool-call fragment: fragments of one call share its `index`. */
|
|
61
|
+
export declare const StreamToolCall: Schema.Struct<{
|
|
62
|
+
readonly index: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
63
|
+
readonly id: Schema.optionalKey<Schema.NullOr<Schema.brand<Schema.String, "ToolCallId">>>;
|
|
64
|
+
readonly function: Schema.optionalKey<Schema.NullOr<Schema.Struct<{
|
|
65
|
+
readonly name: Schema.optionalKey<Schema.NullOr<Schema.String>>;
|
|
66
|
+
readonly arguments: Schema.optionalKey<Schema.NullOr<Schema.String>>;
|
|
67
|
+
}>>>;
|
|
68
|
+
}>;
|
|
69
|
+
export type StreamToolCall = typeof StreamToolCall.Type;
|
|
70
|
+
/** One `data:` chunk of a streaming chat completion. */
|
|
71
|
+
export declare const StreamChunk: Schema.Struct<{
|
|
72
|
+
readonly choices: Schema.optionalKey<Schema.NullOr<Schema.$Array<Schema.Struct<{
|
|
73
|
+
readonly finish_reason: Schema.optionalKey<Schema.NullOr<Schema.String>>;
|
|
74
|
+
readonly delta: Schema.optionalKey<Schema.NullOr<Schema.Struct<{
|
|
75
|
+
readonly tool_calls: Schema.optionalKey<Schema.NullOr<Schema.$Array<Schema.Struct<{
|
|
76
|
+
readonly index: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
77
|
+
readonly id: Schema.optionalKey<Schema.NullOr<Schema.brand<Schema.String, "ToolCallId">>>;
|
|
78
|
+
readonly function: Schema.optionalKey<Schema.NullOr<Schema.Struct<{
|
|
79
|
+
readonly name: Schema.optionalKey<Schema.NullOr<Schema.String>>;
|
|
80
|
+
readonly arguments: Schema.optionalKey<Schema.NullOr<Schema.String>>;
|
|
81
|
+
}>>>;
|
|
82
|
+
}>>>>;
|
|
83
|
+
readonly content: Schema.optionalKey<Schema.NullOr<Schema.String>>;
|
|
84
|
+
readonly reasoning_content: Schema.optionalKey<Schema.NullOr<Schema.String>>;
|
|
85
|
+
readonly reasoning: Schema.optionalKey<Schema.NullOr<Schema.String>>;
|
|
86
|
+
}>>>;
|
|
87
|
+
}>>>>;
|
|
88
|
+
readonly usage: Schema.optionalKey<Schema.NullOr<Schema.Struct<{
|
|
89
|
+
readonly prompt_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
90
|
+
readonly completion_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
91
|
+
readonly total_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
92
|
+
/** DeepSeek-style cache accounting. */
|
|
93
|
+
readonly prompt_cache_hit_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
94
|
+
/** Moonshot-style cache accounting. */
|
|
95
|
+
readonly cached_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
96
|
+
/** OpenAI-native cache reads; gateways (OpenCode's among them) add the cache writes. */
|
|
97
|
+
readonly prompt_tokens_details: Schema.optionalKey<Schema.NullOr<Schema.Struct<{
|
|
98
|
+
readonly cached_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
99
|
+
readonly cache_write_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
100
|
+
}>>>;
|
|
101
|
+
/** OpenAI-native reasoning accounting, counted inside `completion_tokens`. */
|
|
102
|
+
readonly completion_tokens_details: Schema.optionalKey<Schema.NullOr<Schema.Struct<{
|
|
103
|
+
readonly reasoning_tokens: Schema.optionalKey<Schema.NullOr<Schema.Number>>;
|
|
104
|
+
}>>>;
|
|
105
|
+
}>>>;
|
|
106
|
+
readonly error: Schema.optionalKey<Schema.Unknown>;
|
|
107
|
+
}>;
|
|
108
|
+
export type StreamChunk = typeof StreamChunk.Type;
|
|
109
|
+
/** The OpenAI error object a gateway reports a failure with: its type, its code and its message. */
|
|
110
|
+
export declare const GatewayError: Schema.Struct<{
|
|
111
|
+
readonly type: Schema.optionalKey<Schema.NullOr<Schema.String>>;
|
|
112
|
+
readonly code: Schema.optionalKey<Schema.NullOr<Schema.Union<readonly [Schema.String, Schema.Number]>>>;
|
|
113
|
+
readonly message: Schema.optionalKey<Schema.NullOr<Schema.String>>;
|
|
114
|
+
}>;
|
|
115
|
+
export type GatewayError = typeof GatewayError.Type;
|
|
116
|
+
/** A failed request's body in the OpenAI error shape. */
|
|
117
|
+
export declare const GatewayErrorBody: Schema.Struct<{
|
|
118
|
+
readonly error: Schema.Struct<{
|
|
119
|
+
readonly type: Schema.optionalKey<Schema.NullOr<Schema.String>>;
|
|
120
|
+
readonly code: Schema.optionalKey<Schema.NullOr<Schema.Union<readonly [Schema.String, Schema.Number]>>>;
|
|
121
|
+
readonly message: Schema.optionalKey<Schema.NullOr<Schema.String>>;
|
|
122
|
+
}>;
|
|
123
|
+
}>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { AiError } from "effect/ai";
|
|
2
|
+
import { Effect, Option, Schema } from "effect";
|
|
3
|
+
import { ChatCompletion, GatewayError, StreamChunk } from "./chat-wire.entity.js";
|
|
4
|
+
/** A completion body, decoded; one that is no JSON or of the wrong shape is the provider's `InvalidResponseError`. */
|
|
5
|
+
export declare const decodeChatCompletion: (moduleName: string, text: string) => Effect.Effect<ChatCompletion, AiError.AiError>;
|
|
6
|
+
/** A `data:` line's chunk, decoded; one that is no JSON object of the chunk's shape (`null` included) is the provider's `InvalidResponseError`. */
|
|
7
|
+
export declare const decodeStreamChunk: (moduleName: string, payload: string) => Effect.Effect<StreamChunk, AiError.AiError>;
|
|
8
|
+
/** A tool call's arguments, parsed: the model's own output, so a failure is the caller's to name. */
|
|
9
|
+
export declare const decodeToolArguments: (text: string) => Effect.Effect<Schema.Json, Schema.SchemaError>;
|
|
10
|
+
/** The OpenAI error object of a failed request's body, when it has one. */
|
|
11
|
+
export declare const gatewayErrorOf: (body: string) => Option.Option<GatewayError>;
|
|
12
|
+
/** The OpenAI error object of a stream's error chunk, when it is one. */
|
|
13
|
+
export declare const chunkErrorOf: (error: unknown) => Option.Option<GatewayError>;
|
|
14
|
+
/**
|
|
15
|
+
* The error types and codes gateways report an exhausted balance or quota
|
|
16
|
+
* with: OpenCode's `CreditsError`, OpenAI's `insufficient_quota` and
|
|
17
|
+
* `billing_insufficient_balance`, Moonshot's `exceeded_current_quota_error`.
|
|
18
|
+
*/
|
|
19
|
+
export declare const QUOTA_ERRORS: ReadonlySet<string>;
|
|
20
|
+
/**
|
|
21
|
+
* Whether a failure is an exhausted balance or quota, which no retry and no
|
|
22
|
+
* fallback model fixes: a 402 (Payment Required), or an error whose type or
|
|
23
|
+
* code names one (`QUOTA_ERRORS`), at any status. A plain 429 is not: it is
|
|
24
|
+
* a rate limit, retried. The message text is not read.
|
|
25
|
+
*/
|
|
26
|
+
export declare const isQuotaExhausted: (status: Option.Option<number>, error: Option.Option<GatewayError>) => boolean;
|
|
27
|
+
/** Whether a gateway rejected the selected model (OpenCode's `ModelError`), whatever status it answered with. */
|
|
28
|
+
export declare const isModelRejection: (error: GatewayError) => boolean;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AiError } from "effect/ai";
|
|
2
|
+
import { Effect, Option, Schema } from "effect";
|
|
3
|
+
import { ChatCompletion, GatewayError, GatewayErrorBody, StreamChunk } from "./chat-wire.entity.js";
|
|
4
|
+
const invalidResponse = (moduleName, method, what) => (error) => AiError.make({ module: moduleName, method, reason: new AiError.InvalidResponseError({ description: `${what}: ${error.message}` }) }), completion = Schema.decodeUnknownEffect(Schema.fromJsonString(ChatCompletion)), chunk = Schema.decodeUnknownEffect(Schema.fromJsonString(StreamChunk)), json = Schema.decodeUnknownEffect(Schema.fromJsonString(Schema.Json)), errorBody = Schema.decodeUnknownOption(Schema.fromJsonString(GatewayErrorBody)), errorObject = Schema.decodeUnknownOption(GatewayError);
|
|
5
|
+
export const decodeChatCompletion = (moduleName, text) => completion(text).pipe(Effect.mapError(invalidResponse(moduleName, "generateText", "the completion body is not a chat completion"))), decodeStreamChunk = (moduleName, payload) => chunk(payload).pipe(Effect.mapError(invalidResponse(moduleName, "streamText", `a data: line is not a completion chunk (${payload.slice(0, 200)})`))), decodeToolArguments = (text) => json(text), gatewayErrorOf = (body) => Option.map(errorBody(body), (decoded) => decoded.error), chunkErrorOf = (error) => errorObject(error), QUOTA_ERRORS = new Set([
|
|
6
|
+
"CreditsError",
|
|
7
|
+
"insufficient_quota",
|
|
8
|
+
"billing_insufficient_balance",
|
|
9
|
+
"exceeded_current_quota_error"
|
|
10
|
+
]), isQuotaExhausted = (status, error) => Option.contains(status, 402) || Option.exists(error, (found) => [found.type, found.code].some((name) => typeof name === "string" && QUOTA_ERRORS.has(name))), isModelRejection = (error) => error.type === "ModelError" || /model .*not supported/i.test(error.message ?? "");
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { ToolCallId } from "@xandreed/core";
|
|
3
|
+
const Count = Schema.optionalKey(Schema.NullOr(Schema.Number)), Text = Schema.optionalKey(Schema.NullOr(Schema.String));
|
|
4
|
+
export const CompletionUsage = Schema.Struct({
|
|
5
|
+
prompt_tokens: Count,
|
|
6
|
+
completion_tokens: Count,
|
|
7
|
+
total_tokens: Count,
|
|
8
|
+
prompt_cache_hit_tokens: Count,
|
|
9
|
+
cached_tokens: Count,
|
|
10
|
+
prompt_tokens_details: Schema.optionalKey(Schema.NullOr(Schema.Struct({ cached_tokens: Count, cache_write_tokens: Count }))),
|
|
11
|
+
completion_tokens_details: Schema.optionalKey(Schema.NullOr(Schema.Struct({ reasoning_tokens: Count })))
|
|
12
|
+
});
|
|
13
|
+
const WireToolCallId = Schema.optionalKey(Schema.NullOr(ToolCallId)), ToolCallFunction = Schema.optionalKey(Schema.NullOr(Schema.Struct({ name: Text, arguments: Text }))), MessageText = { content: Text, reasoning_content: Text, reasoning: Text };
|
|
14
|
+
export const ChatCompletion = Schema.Struct({
|
|
15
|
+
choices: Schema.optionalKey(Schema.NullOr(Schema.Array(Schema.Struct({
|
|
16
|
+
finish_reason: Text,
|
|
17
|
+
message: Schema.optionalKey(Schema.NullOr(Schema.Struct({
|
|
18
|
+
...MessageText,
|
|
19
|
+
tool_calls: Schema.optionalKey(Schema.NullOr(Schema.Array(Schema.Struct({ id: WireToolCallId, function: ToolCallFunction }))))
|
|
20
|
+
})))
|
|
21
|
+
})))),
|
|
22
|
+
usage: Schema.optionalKey(Schema.NullOr(CompletionUsage))
|
|
23
|
+
}), StreamToolCall = Schema.Struct({ index: Count, id: WireToolCallId, function: ToolCallFunction }), StreamChunk = Schema.Struct({
|
|
24
|
+
choices: Schema.optionalKey(Schema.NullOr(Schema.Array(Schema.Struct({
|
|
25
|
+
finish_reason: Text,
|
|
26
|
+
delta: Schema.optionalKey(Schema.NullOr(Schema.Struct({
|
|
27
|
+
...MessageText,
|
|
28
|
+
tool_calls: Schema.optionalKey(Schema.NullOr(Schema.Array(StreamToolCall)))
|
|
29
|
+
})))
|
|
30
|
+
})))),
|
|
31
|
+
usage: Schema.optionalKey(Schema.NullOr(CompletionUsage)),
|
|
32
|
+
error: Schema.optionalKey(Schema.Unknown)
|
|
33
|
+
}), GatewayError = Schema.Struct({
|
|
34
|
+
type: Text,
|
|
35
|
+
code: Schema.optionalKey(Schema.NullOr(Schema.Union([Schema.String, Schema.Number]))),
|
|
36
|
+
message: Text
|
|
37
|
+
}), GatewayErrorBody = Schema.Struct({ error: GatewayError });
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import type { Tracer } from "effect";
|
|
3
|
+
/** Annotate the actual native operation, never a routing or engine wrapper. */
|
|
4
|
+
export declare const annotateCompatRequest: (span: Tracer.Span, provider: string, model: string) => Effect.Effect<void>;
|
|
5
|
+
/** Raw cumulative fields replace their own attributes; missing counts never become zero. */
|
|
6
|
+
export declare const observeCompatUsage: (span: Tracer.Span, raw: unknown, final: boolean, reasoning?: boolean) => Effect.Effect<void>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Effect, Option } from "effect";
|
|
2
|
+
import { providerUsageCounts } from "./model-usage.entity.functions.js";
|
|
3
|
+
export const annotateCompatRequest = (span, provider, model) => Effect.sync(() => {
|
|
4
|
+
span.attribute("gen_ai.provider.name", provider);
|
|
5
|
+
span.attribute("gen_ai.request.model", model);
|
|
6
|
+
span.attribute("gen_ai.operation.name", "chat");
|
|
7
|
+
}), observeCompatUsage = (span, raw, final, reasoning = !0) => Option.match(providerUsageCounts("compat", raw, reasoning), {
|
|
8
|
+
onNone: () => Effect.void,
|
|
9
|
+
onSome: (counts) => Effect.sync(() => {
|
|
10
|
+
[
|
|
11
|
+
["gen_ai.usage.input_tokens", counts.inputTokens],
|
|
12
|
+
["gen_ai.usage.output_tokens", !final && counts.outputTokens === 0 ? void 0 : counts.outputTokens],
|
|
13
|
+
["gen_ai.usage.cache_read.input_tokens", counts.cacheReadTokens],
|
|
14
|
+
["gen_ai.usage.cache_write.input_tokens", counts.cacheWriteTokens],
|
|
15
|
+
["gen_ai.usage.reasoning.output_tokens", counts.reasoningTokens]
|
|
16
|
+
].forEach(([name, value]) => {
|
|
17
|
+
if (value !== void 0 && Number.isSafeInteger(value))
|
|
18
|
+
span.attribute(name, value);
|
|
19
|
+
});
|
|
20
|
+
})
|
|
21
|
+
});
|
package/dist/llm/compat.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { AiError, LanguageModel, Tool } from "effect/ai";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import type {
|
|
1
|
+
import { AiError, LanguageModel, Prompt, Tool, type Response as ResponseParts } from "effect/ai";
|
|
2
|
+
import { Effect, Schema } from "effect";
|
|
3
|
+
import type { UsageReading } from "./sse.js";
|
|
4
|
+
import type { ModelsFetch } from "../ports/models-transport.port.js";
|
|
5
5
|
/**
|
|
6
6
|
* A generic OpenAI-compatible `/chat/completions` `LanguageModel` over raw
|
|
7
7
|
* `fetch`. The official `@effect/ai-openai` client targets api.openai.com;
|
|
@@ -10,10 +10,11 @@ import type { CompletionUsage } from "./sse.js";
|
|
|
10
10
|
*
|
|
11
11
|
* `generateText` consumes whole turns (`stream: false`); `streamText` runs
|
|
12
12
|
* the same request with `stream: true` through the pure SSE state machine in
|
|
13
|
-
* `sse.ts`.
|
|
14
|
-
*
|
|
13
|
+
* `sse.ts`. Both paths use the same bounded request policy before handing
|
|
14
|
+
* off a successful response; body and SSE failures after handoff are final.
|
|
15
15
|
*/
|
|
16
16
|
type Json = Record<string, unknown>;
|
|
17
|
+
/** @public */
|
|
17
18
|
export interface CompatConfig {
|
|
18
19
|
/** Module name for `AiError` provenance (e.g. "OpenCode"). */
|
|
19
20
|
readonly moduleName: string;
|
|
@@ -32,16 +33,55 @@ export interface CompatConfig {
|
|
|
32
33
|
* thinking defaults or call policy — for endpoints that reject the
|
|
33
34
|
* gateway extensions (Gemini's OpenAI-compatible API). */
|
|
34
35
|
readonly standardOnly?: boolean;
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
/** How the endpoint's usage is read (default: every count, reasoning
|
|
37
|
+
* included; see `UsageReading`). */
|
|
38
|
+
readonly usage?: UsageReading;
|
|
39
|
+
/** The model reads images: send them as `image_url` parts (default false: label lines; see `toChatMessages`). */
|
|
40
|
+
readonly images?: boolean;
|
|
41
|
+
/** The fetch requests go through (the models plugin supplies its chosen transport); otherwise uses the call-local FetchHttpClient.Fetch reference. */
|
|
42
|
+
readonly fetchImpl?: ModelsFetch;
|
|
43
|
+
/** Host-owned admission (e.g. reserve a worst-case cost) before every
|
|
44
|
+
* physical send: the first and each retry the request policy makes. A
|
|
45
|
+
* failure ends the call without that send. */
|
|
38
46
|
readonly beforeRequest?: (body: Readonly<Record<string, unknown>>) => Effect.Effect<void, AiError.AiError>;
|
|
39
|
-
/** Per-call accounting/resource boundary
|
|
40
|
-
*
|
|
47
|
+
/** Per-call accounting/resource boundary around the whole logical call,
|
|
48
|
+
* its retries included: it sees the final response or failure once.
|
|
49
|
+
* Non-streaming responses are fully buffered inside request so
|
|
50
|
+
* interruption also aborts body consumption. */
|
|
41
51
|
readonly aroundRequest?: (body: Readonly<Record<string, unknown>>, request: Effect.Effect<Response, AiError.AiError>) => Effect.Effect<Response, AiError.AiError>;
|
|
42
52
|
}
|
|
43
|
-
/**
|
|
44
|
-
|
|
53
|
+
/**
|
|
54
|
+
* How `toChatMessages` writes a request.
|
|
55
|
+
*
|
|
56
|
+
* @public
|
|
57
|
+
*/
|
|
58
|
+
export interface ChatMessageOptions {
|
|
59
|
+
/** Send images as `image_url` parts (default false: label lines). */
|
|
60
|
+
readonly images?: boolean;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Decoded `Prompt` messages → chat-completions message objects. A
|
|
64
|
+
* text-only user message is its text, as before. Without `images`, a user
|
|
65
|
+
* message is one string: its text, then a label line per file part, and a
|
|
66
|
+
* tool result is its text with a label line per image. With `images`, a
|
|
67
|
+
* user message with images is a content array of its parts in order (an
|
|
68
|
+
* image as an `image_url` part with a `data:` URL), and a tool result is its
|
|
69
|
+
* text in the tool message, while its images, which a tool message cannot
|
|
70
|
+
* carry, follow all the batch's tool messages in one user message, each
|
|
71
|
+
* result's captioned `Images returned by <tool> (call <id>):`; a user
|
|
72
|
+
* message right after the batch is merged into it, so no two user messages
|
|
73
|
+
* follow each other, captioned `The user's message:` when it has images of
|
|
74
|
+
* its own.
|
|
75
|
+
*/
|
|
76
|
+
export declare const toChatMessages: (prompt: Prompt.Prompt, options?: ChatMessageOptions) => ReadonlyArray<Json>;
|
|
77
|
+
/**
|
|
78
|
+
* A prompt with every file part a label line: what a route that reads no
|
|
79
|
+
* images is sent (the models plugin rewrites a request so on such routes).
|
|
80
|
+
* A user message with file parts becomes one text part (its text, then a
|
|
81
|
+
* label line each); a tool result with model output becomes its text with a
|
|
82
|
+
* label line per file part, as its result. Messages without files are kept.
|
|
83
|
+
*/
|
|
84
|
+
export declare const promptWithImageLabels: (prompt: Prompt.Prompt) => Prompt.Prompt;
|
|
45
85
|
/** User-defined tools → chat-completions function declarations (empty when
|
|
46
86
|
* the request carries none — the caller omits the `tools` key entirely). */
|
|
47
87
|
export declare const toChatTools: (tools: ReadonlyArray<Tool.Any>) => ReadonlyArray<Json>;
|
|
@@ -54,28 +94,39 @@ export declare const toChatTools: (tools: ReadonlyArray<Tool.Any>) => ReadonlyAr
|
|
|
54
94
|
* `thinking: {type: "enabled"}`; qwen takes `enable_thinking`. Disabling is
|
|
55
95
|
* never sent (kimi-k2.7+ rejects it outright); unknown families get nothing.
|
|
56
96
|
*/
|
|
57
|
-
export declare const thinkingParams: (model: string) =>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
97
|
+
export declare const thinkingParams: (model: string) => Schema.JsonObject;
|
|
98
|
+
/** Parse one non-streaming completion into `@effect/ai` encoded parts. */
|
|
99
|
+
export declare const fromChatCompletion: (moduleName: string, body: {
|
|
100
|
+
readonly usage?: {
|
|
101
|
+
readonly prompt_tokens_details?: {
|
|
102
|
+
readonly cached_tokens?: number | null;
|
|
103
|
+
readonly cache_write_tokens?: number | null;
|
|
104
|
+
} | null;
|
|
105
|
+
readonly cached_tokens?: number | null;
|
|
106
|
+
readonly completion_tokens_details?: {
|
|
107
|
+
readonly reasoning_tokens?: number | null;
|
|
108
|
+
} | null;
|
|
109
|
+
readonly prompt_tokens?: number | null;
|
|
110
|
+
readonly completion_tokens?: number | null;
|
|
111
|
+
readonly total_tokens?: number | null;
|
|
112
|
+
readonly prompt_cache_hit_tokens?: number | null;
|
|
113
|
+
} | null;
|
|
114
|
+
readonly choices?: readonly {
|
|
61
115
|
readonly message?: {
|
|
62
|
-
readonly content?: string | null;
|
|
63
|
-
/** DeepSeek-native vocabulary (kimi-k2.7-code, deepseek serve this). */
|
|
64
|
-
readonly reasoning_content?: string | null;
|
|
65
|
-
/** OpenRouter-style vocabulary (kimi-k2.6 via Moonshot serves this). */
|
|
66
116
|
readonly reasoning?: string | null;
|
|
67
|
-
readonly
|
|
68
|
-
|
|
117
|
+
readonly content?: string | null;
|
|
118
|
+
readonly tool_calls?: readonly {
|
|
69
119
|
readonly function?: {
|
|
70
|
-
readonly name?: string;
|
|
71
|
-
readonly arguments?: string;
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
120
|
+
readonly name?: string | null;
|
|
121
|
+
readonly arguments?: string | null;
|
|
122
|
+
} | null;
|
|
123
|
+
readonly id?: (string & import("effect/Brand").Brand<"ToolCallId">) | null;
|
|
124
|
+
}[] | null;
|
|
125
|
+
readonly reasoning_content?: string | null;
|
|
126
|
+
} | null;
|
|
127
|
+
readonly finish_reason?: string | null;
|
|
128
|
+
}[] | null;
|
|
129
|
+
}, reading?: UsageReading | undefined) => Effect.Effect<readonly ResponseParts.PartEncoded[], AiError.AiError, never>;
|
|
130
|
+
/** @public */
|
|
80
131
|
export declare const makeCompatLanguageModel: (config: CompatConfig) => Effect.Effect<LanguageModel.LanguageModel>;
|
|
81
132
|
export {};
|