@graphorin/provider 0.5.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/CHANGELOG.md +6 -0
- package/LICENSE +21 -0
- package/README.md +105 -0
- package/dist/adapters/index.d.ts +4 -0
- package/dist/adapters/index.js +6 -0
- package/dist/adapters/llamacpp-server.d.ts +69 -0
- package/dist/adapters/llamacpp-server.d.ts.map +1 -0
- package/dist/adapters/llamacpp-server.js +51 -0
- package/dist/adapters/llamacpp-server.js.map +1 -0
- package/dist/adapters/ollama.d.ts +43 -0
- package/dist/adapters/ollama.d.ts.map +1 -0
- package/dist/adapters/ollama.js +246 -0
- package/dist/adapters/ollama.js.map +1 -0
- package/dist/adapters/openai-compatible.d.ts +48 -0
- package/dist/adapters/openai-compatible.d.ts.map +1 -0
- package/dist/adapters/openai-compatible.js +28 -0
- package/dist/adapters/openai-compatible.js.map +1 -0
- package/dist/adapters/vercel.d.ts +135 -0
- package/dist/adapters/vercel.d.ts.map +1 -0
- package/dist/adapters/vercel.js +306 -0
- package/dist/adapters/vercel.js.map +1 -0
- package/dist/counters/anthropic.d.ts +36 -0
- package/dist/counters/anthropic.d.ts.map +1 -0
- package/dist/counters/anthropic.js +63 -0
- package/dist/counters/anthropic.js.map +1 -0
- package/dist/counters/bedrock.d.ts +27 -0
- package/dist/counters/bedrock.d.ts.map +1 -0
- package/dist/counters/bedrock.js +29 -0
- package/dist/counters/bedrock.js.map +1 -0
- package/dist/counters/dispatcher.d.ts +34 -0
- package/dist/counters/dispatcher.d.ts.map +1 -0
- package/dist/counters/dispatcher.js +78 -0
- package/dist/counters/dispatcher.js.map +1 -0
- package/dist/counters/global.d.ts +26 -0
- package/dist/counters/global.d.ts.map +1 -0
- package/dist/counters/global.js +31 -0
- package/dist/counters/global.js.map +1 -0
- package/dist/counters/google.d.ts +27 -0
- package/dist/counters/google.d.ts.map +1 -0
- package/dist/counters/google.js +29 -0
- package/dist/counters/google.js.map +1 -0
- package/dist/counters/heuristic.d.ts +37 -0
- package/dist/counters/heuristic.d.ts.map +1 -0
- package/dist/counters/heuristic.js +64 -0
- package/dist/counters/heuristic.js.map +1 -0
- package/dist/counters/index.d.ts +9 -0
- package/dist/counters/index.js +10 -0
- package/dist/counters/js-tiktoken.d.ts +63 -0
- package/dist/counters/js-tiktoken.d.ts.map +1 -0
- package/dist/counters/js-tiktoken.js +73 -0
- package/dist/counters/js-tiktoken.js.map +1 -0
- package/dist/counters/serialize.d.ts +37 -0
- package/dist/counters/serialize.d.ts.map +1 -0
- package/dist/counters/serialize.js +57 -0
- package/dist/counters/serialize.js.map +1 -0
- package/dist/errors/errors.d.ts +167 -0
- package/dist/errors/errors.d.ts.map +1 -0
- package/dist/errors/errors.js +182 -0
- package/dist/errors/errors.js.map +1 -0
- package/dist/errors/index.d.ts +2 -0
- package/dist/errors/index.js +3 -0
- package/dist/index.d.ts +69 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +70 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/abort.js +34 -0
- package/dist/internal/abort.js.map +1 -0
- package/dist/internal/http.js +163 -0
- package/dist/internal/http.js.map +1 -0
- package/dist/internal/openai-shaped.js +308 -0
- package/dist/internal/openai-shaped.js.map +1 -0
- package/dist/internal/sse.js +104 -0
- package/dist/internal/sse.js.map +1 -0
- package/dist/internal/url-utils.js +24 -0
- package/dist/internal/url-utils.js.map +1 -0
- package/dist/middleware/compose.d.ts +85 -0
- package/dist/middleware/compose.d.ts.map +1 -0
- package/dist/middleware/compose.js +174 -0
- package/dist/middleware/compose.js.map +1 -0
- package/dist/middleware/index.d.ts +10 -0
- package/dist/middleware/index.js +11 -0
- package/dist/middleware/production-hook.d.ts +27 -0
- package/dist/middleware/production-hook.d.ts.map +1 -0
- package/dist/middleware/production-hook.js +21 -0
- package/dist/middleware/production-hook.js.map +1 -0
- package/dist/middleware/with-cost-limit.d.ts +46 -0
- package/dist/middleware/with-cost-limit.d.ts.map +1 -0
- package/dist/middleware/with-cost-limit.js +66 -0
- package/dist/middleware/with-cost-limit.js.map +1 -0
- package/dist/middleware/with-cost-tracking.d.ts +91 -0
- package/dist/middleware/with-cost-tracking.d.ts.map +1 -0
- package/dist/middleware/with-cost-tracking.js +95 -0
- package/dist/middleware/with-cost-tracking.js.map +1 -0
- package/dist/middleware/with-fallback.d.ts +25 -0
- package/dist/middleware/with-fallback.d.ts.map +1 -0
- package/dist/middleware/with-fallback.js +84 -0
- package/dist/middleware/with-fallback.js.map +1 -0
- package/dist/middleware/with-rate-limit.d.ts +36 -0
- package/dist/middleware/with-rate-limit.d.ts.map +1 -0
- package/dist/middleware/with-rate-limit.js +141 -0
- package/dist/middleware/with-rate-limit.js.map +1 -0
- package/dist/middleware/with-redaction.d.ts +60 -0
- package/dist/middleware/with-redaction.d.ts.map +1 -0
- package/dist/middleware/with-redaction.js +305 -0
- package/dist/middleware/with-redaction.js.map +1 -0
- package/dist/middleware/with-retry.d.ts +26 -0
- package/dist/middleware/with-retry.d.ts.map +1 -0
- package/dist/middleware/with-retry.js +157 -0
- package/dist/middleware/with-retry.js.map +1 -0
- package/dist/middleware/with-tracing.d.ts +21 -0
- package/dist/middleware/with-tracing.d.ts.map +1 -0
- package/dist/middleware/with-tracing.js +55 -0
- package/dist/middleware/with-tracing.js.map +1 -0
- package/dist/model-tier/classify.d.ts +34 -0
- package/dist/model-tier/classify.d.ts.map +1 -0
- package/dist/model-tier/classify.js +117 -0
- package/dist/model-tier/classify.js.map +1 -0
- package/dist/model-tier/index.d.ts +2 -0
- package/dist/model-tier/index.js +3 -0
- package/dist/provider.d.ts +51 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +64 -0
- package/dist/provider.js.map +1 -0
- package/dist/reasoning/apply-policy.d.ts +22 -0
- package/dist/reasoning/apply-policy.d.ts.map +1 -0
- package/dist/reasoning/apply-policy.js +47 -0
- package/dist/reasoning/apply-policy.js.map +1 -0
- package/dist/reasoning/classify-contract.d.ts +41 -0
- package/dist/reasoning/classify-contract.d.ts.map +1 -0
- package/dist/reasoning/classify-contract.js +59 -0
- package/dist/reasoning/classify-contract.js.map +1 -0
- package/dist/reasoning/index.d.ts +4 -0
- package/dist/reasoning/index.js +5 -0
- package/dist/reasoning/retention.d.ts +39 -0
- package/dist/reasoning/retention.d.ts.map +1 -0
- package/dist/reasoning/retention.js +33 -0
- package/dist/reasoning/retention.js.map +1 -0
- package/dist/tool-examples.d.ts +14 -0
- package/dist/tool-examples.d.ts.map +1 -0
- package/dist/tool-examples.js +35 -0
- package/dist/tool-examples.js.map +1 -0
- package/dist/trust/classify-local-provider.d.ts +47 -0
- package/dist/trust/classify-local-provider.d.ts.map +1 -0
- package/dist/trust/classify-local-provider.js +164 -0
- package/dist/trust/classify-local-provider.js.map +1 -0
- package/dist/trust/index.d.ts +3 -0
- package/dist/trust/index.js +3 -0
- package/package.json +128 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-compatible.d.ts","names":[],"sources":["../../src/adapters/openai-compatible.ts"],"sourcesContent":[],"mappings":";;;;;;;;;UAwBiB,8BAAA;;;;;;;;;;;;;qBAaI,SAAS;;8BAEA;;;;;;;gCAOE,cAAc;;;;;;;;;;;;;iBAc9B,uBAAA,UAAiC,iCAAiC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { buildOpenAIShapedProvider } from "../internal/openai-shaped.js";
|
|
2
|
+
|
|
3
|
+
//#region src/adapters/openai-compatible.ts
|
|
4
|
+
/**
|
|
5
|
+
* Build a Graphorin {@link Provider} backed by an OpenAI-compatible
|
|
6
|
+
* HTTP server. The same code path serves LMStudio, LocalAI, vLLM, and
|
|
7
|
+
* any other compatible self-host endpoint.
|
|
8
|
+
*
|
|
9
|
+
* @stable
|
|
10
|
+
*/
|
|
11
|
+
function openAICompatibleAdapter(options) {
|
|
12
|
+
return buildOpenAIShapedProvider({
|
|
13
|
+
providerName: options.name ?? `openai-compatible-${options.model}`,
|
|
14
|
+
model: options.model,
|
|
15
|
+
baseUrl: options.baseUrl,
|
|
16
|
+
...options.chatPath !== void 0 ? { chatPath: options.chatPath } : {},
|
|
17
|
+
...options.apiKey !== void 0 ? { apiKey: options.apiKey } : {},
|
|
18
|
+
...options.headers !== void 0 ? { headers: options.headers } : {},
|
|
19
|
+
...options.fetchImpl !== void 0 ? { fetchImpl: options.fetchImpl } : {},
|
|
20
|
+
...options.allowInsecureTransport !== void 0 ? { allowInsecureTransport: options.allowInsecureTransport } : {},
|
|
21
|
+
...options.acceptsSensitivity !== void 0 ? { acceptsSensitivity: options.acceptsSensitivity } : {},
|
|
22
|
+
...options.logger !== void 0 ? { logger: options.logger } : {}
|
|
23
|
+
}).provider;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//#endregion
|
|
27
|
+
export { openAICompatibleAdapter };
|
|
28
|
+
//# sourceMappingURL=openai-compatible.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-compatible.js","names":[],"sources":["../../src/adapters/openai-compatible.ts"],"sourcesContent":["/**\n * Generic OpenAI-compatible adapter — works against any HTTP server\n * that speaks the `/v1/chat/completions` REST contract. Tested\n * deployments include LMStudio (default port 1234), LocalAI (default\n * port 8080), vLLM (`python -m vllm.entrypoints.openai.api_server`,\n * default port 8000), Together-style self-host endpoints, and any\n * other server in the OpenAI-compatible ecosystem.\n *\n * The adapter shares the same `LocalProviderTrust` classifier as\n * `ollamaAdapter` and `llamaCppServerAdapter` — one classifier, one\n * policy table, one error type.\n *\n * @packageDocumentation\n */\n\nimport type { Provider, Sensitivity } from '@graphorin/core';\n\nimport { buildOpenAIShapedProvider } from '../internal/openai-shaped.js';\n\n/**\n * Options accepted by {@link openAICompatibleAdapter}.\n *\n * @stable\n */\nexport interface OpenAICompatibleAdapterOptions {\n /** Model identifier sent in the request body's `model` field. */\n readonly model: string;\n /**\n * Base URL of the OpenAI-compatible server. The classifier inspects\n * the protocol + host to assign a `LocalProviderTrust` value.\n */\n readonly baseUrl: string;\n /** Optional REST path override. Defaults to `/v1/chat/completions`. */\n readonly chatPath?: string;\n /** Optional bearer-auth API key. */\n readonly apiKey?: string;\n /** Extra headers merged on top of `content-type` + `accept` defaults. */\n readonly headers?: Readonly<Record<string, string>>;\n /** Custom `fetch` implementation; useful for tests. */\n readonly fetchImpl?: typeof fetch;\n /**\n * Acknowledge the risk of running over plaintext HTTP against a\n * public host.\n */\n readonly allowInsecureTransport?: boolean;\n /** Override for the default `acceptsSensitivity` value. */\n readonly acceptsSensitivity?: ReadonlyArray<Sensitivity>;\n /** Provider name attached to spans / log lines. */\n readonly name?: string;\n /** Optional log sink. */\n readonly logger?: (level: 'warn' | 'info', message: string, meta?: object) => void;\n}\n\n/**\n * Build a Graphorin {@link Provider} backed by an OpenAI-compatible\n * HTTP server. The same code path serves LMStudio, LocalAI, vLLM, and\n * any other compatible self-host endpoint.\n *\n * @stable\n */\nexport function openAICompatibleAdapter(options: OpenAICompatibleAdapterOptions): Provider {\n const providerName = options.name ?? `openai-compatible-${options.model}`;\n const built = buildOpenAIShapedProvider({\n providerName,\n model: options.model,\n baseUrl: options.baseUrl,\n ...(options.chatPath !== undefined ? { chatPath: options.chatPath } : {}),\n ...(options.apiKey !== undefined ? { apiKey: options.apiKey } : {}),\n ...(options.headers !== undefined ? { headers: options.headers } : {}),\n ...(options.fetchImpl !== undefined ? { fetchImpl: options.fetchImpl } : {}),\n ...(options.allowInsecureTransport !== undefined\n ? { allowInsecureTransport: options.allowInsecureTransport }\n : {}),\n ...(options.acceptsSensitivity !== undefined\n ? { acceptsSensitivity: options.acceptsSensitivity }\n : {}),\n ...(options.logger !== undefined ? { logger: options.logger } : {}),\n });\n return built.provider;\n}\n"],"mappings":";;;;;;;;;;AA4DA,SAAgB,wBAAwB,SAAmD;AAkBzF,QAhBc,0BAA0B;EACtC,cAFmB,QAAQ,QAAQ,qBAAqB,QAAQ;EAGhE,OAAO,QAAQ;EACf,SAAS,QAAQ;EACjB,GAAI,QAAQ,aAAa,SAAY,EAAE,UAAU,QAAQ,UAAU,GAAG,EAAE;EACxE,GAAI,QAAQ,WAAW,SAAY,EAAE,QAAQ,QAAQ,QAAQ,GAAG,EAAE;EAClE,GAAI,QAAQ,YAAY,SAAY,EAAE,SAAS,QAAQ,SAAS,GAAG,EAAE;EACrE,GAAI,QAAQ,cAAc,SAAY,EAAE,WAAW,QAAQ,WAAW,GAAG,EAAE;EAC3E,GAAI,QAAQ,2BAA2B,SACnC,EAAE,wBAAwB,QAAQ,wBAAwB,GAC1D,EAAE;EACN,GAAI,QAAQ,uBAAuB,SAC/B,EAAE,oBAAoB,QAAQ,oBAAoB,GAClD,EAAE;EACN,GAAI,QAAQ,WAAW,SAAY,EAAE,QAAQ,QAAQ,QAAQ,GAAG,EAAE;EACnE,CAAC,CACW"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Message, Provider, ProviderCapabilities, Usage } from "@graphorin/core";
|
|
2
|
+
|
|
3
|
+
//#region src/adapters/vercel.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Structural shape the adapter expects from the AI SDK language model
|
|
7
|
+
* value. The real `LanguageModelV4` matches this shape. Re-declared
|
|
8
|
+
* here so we do not pin a hard dependency on `@ai-sdk/provider`.
|
|
9
|
+
*
|
|
10
|
+
* @stable
|
|
11
|
+
*/
|
|
12
|
+
interface LanguageModelLike {
|
|
13
|
+
readonly provider: string;
|
|
14
|
+
readonly modelId: string;
|
|
15
|
+
readonly specificationVersion?: string | number;
|
|
16
|
+
/**
|
|
17
|
+
* Optional capability flags carried by the AI SDK model. The adapter
|
|
18
|
+
* forwards them onto the canonical `ProviderCapabilities` shape;
|
|
19
|
+
* missing values are filled in with conservative defaults.
|
|
20
|
+
*/
|
|
21
|
+
readonly supportedToolCallTypes?: readonly string[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Loose chunk shape emitted by the AI SDK's `streamText`. The shape is
|
|
25
|
+
* intentionally permissive — we accept anything that carries the
|
|
26
|
+
* fields we use and ignore the rest. This keeps the adapter tolerant
|
|
27
|
+
* of additive AI SDK schema changes.
|
|
28
|
+
*
|
|
29
|
+
* The fields we read are normalized in the adapter via narrow helper
|
|
30
|
+
* functions, so we deliberately type each as `unknown` and gate
|
|
31
|
+
* access behind `typeof` checks at runtime.
|
|
32
|
+
*
|
|
33
|
+
* @stable
|
|
34
|
+
*/
|
|
35
|
+
interface AISDKChunk {
|
|
36
|
+
readonly type: string;
|
|
37
|
+
readonly [extra: string]: unknown;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Subset of the AI SDK surface used by the adapter.
|
|
41
|
+
*
|
|
42
|
+
* @stable
|
|
43
|
+
*/
|
|
44
|
+
interface VercelRuntimeOverrides {
|
|
45
|
+
readonly streamText: (args: {
|
|
46
|
+
model: LanguageModelLike;
|
|
47
|
+
messages: ReadonlyArray<Message>;
|
|
48
|
+
system?: string;
|
|
49
|
+
tools?: unknown;
|
|
50
|
+
toolChoice?: unknown;
|
|
51
|
+
temperature?: number;
|
|
52
|
+
maxTokens?: number;
|
|
53
|
+
abortSignal?: AbortSignal;
|
|
54
|
+
providerOptions?: Readonly<Record<string, unknown>>;
|
|
55
|
+
}) => {
|
|
56
|
+
readonly fullStream: AsyncIterable<AISDKChunk>;
|
|
57
|
+
};
|
|
58
|
+
readonly generateText: (args: {
|
|
59
|
+
model: LanguageModelLike;
|
|
60
|
+
messages: ReadonlyArray<Message>;
|
|
61
|
+
system?: string;
|
|
62
|
+
tools?: unknown;
|
|
63
|
+
toolChoice?: unknown;
|
|
64
|
+
temperature?: number;
|
|
65
|
+
maxTokens?: number;
|
|
66
|
+
abortSignal?: AbortSignal;
|
|
67
|
+
providerOptions?: Readonly<Record<string, unknown>>;
|
|
68
|
+
}) => Promise<{
|
|
69
|
+
readonly text?: string;
|
|
70
|
+
readonly toolCalls?: ReadonlyArray<{
|
|
71
|
+
readonly toolCallId: string;
|
|
72
|
+
readonly toolName: string;
|
|
73
|
+
readonly args: unknown;
|
|
74
|
+
}>;
|
|
75
|
+
readonly usage?: Partial<Usage> & {
|
|
76
|
+
readonly inputTokens?: number;
|
|
77
|
+
readonly outputTokens?: number;
|
|
78
|
+
readonly totalTokens?: number;
|
|
79
|
+
};
|
|
80
|
+
readonly finishReason?: string;
|
|
81
|
+
readonly providerMetadata?: Readonly<Record<string, unknown>>;
|
|
82
|
+
}>;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Options accepted by {@link vercelAdapter}.
|
|
86
|
+
*
|
|
87
|
+
* @stable
|
|
88
|
+
*/
|
|
89
|
+
interface VercelAdapterOptions {
|
|
90
|
+
/**
|
|
91
|
+
* Fully-qualified provider name, used for span / log labelling.
|
|
92
|
+
* Defaults to `${model.provider}-${model.modelId}`.
|
|
93
|
+
*/
|
|
94
|
+
readonly name?: string;
|
|
95
|
+
/**
|
|
96
|
+
* Capability declaration. The adapter merges these on top of a
|
|
97
|
+
* conservative defaults table (`streaming: true`, `toolCalling: true`,
|
|
98
|
+
* `multimodal: true`, …); supply explicit values to narrow them.
|
|
99
|
+
*/
|
|
100
|
+
readonly capabilities?: Partial<ProviderCapabilities>;
|
|
101
|
+
/**
|
|
102
|
+
* Runtime override for the AI SDK functions. When unset, the adapter
|
|
103
|
+
* lazily `await import('ai')` on first call. Test suites pass a
|
|
104
|
+
* fixture-driven implementation directly.
|
|
105
|
+
*/
|
|
106
|
+
readonly runtimeOverrides?: VercelRuntimeOverrides;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Wrap a Vercel AI SDK language-model value in a Graphorin
|
|
110
|
+
* {@link Provider}. The adapter passes Graphorin `Message`s through
|
|
111
|
+
* directly — both formats use the same role + content discriminated
|
|
112
|
+
* shape — and translates the streaming chunks emitted by the AI SDK
|
|
113
|
+
* onto Graphorin `ProviderEvent`s.
|
|
114
|
+
*
|
|
115
|
+
* The adapter auto-detects the model's
|
|
116
|
+
* {@link import('@graphorin/core').ReasoningContract} from its
|
|
117
|
+
* `modelId` (e.g. Anthropic Claude → `'round-trip-required'`,
|
|
118
|
+
* OpenAI o1 / o3 → `'hidden'`, Gemini reasoning variants →
|
|
119
|
+
* `'hidden'`, everything else → `'optional'`). Callers can override
|
|
120
|
+
* the inferred value via `options.capabilities.reasoningContract`.
|
|
121
|
+
*
|
|
122
|
+
* @stable
|
|
123
|
+
*/
|
|
124
|
+
declare function vercelAdapter(model: LanguageModelLike, options?: VercelAdapterOptions): Provider;
|
|
125
|
+
/**
|
|
126
|
+
* Test-only hook that resets the cached AI SDK runtime. Provider tests
|
|
127
|
+
* that mutate the cache (e.g. by injecting a mock then verifying the
|
|
128
|
+
* default loader runs) call this between scenarios.
|
|
129
|
+
*
|
|
130
|
+
* @internal
|
|
131
|
+
*/
|
|
132
|
+
declare function __resetVercelRuntimeCache(): void;
|
|
133
|
+
//#endregion
|
|
134
|
+
export { AISDKChunk, LanguageModelLike, VercelAdapterOptions, VercelRuntimeOverrides, __resetVercelRuntimeCache, vercelAdapter };
|
|
135
|
+
//# sourceMappingURL=vercel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vercel.d.ts","names":[],"sources":["../../src/adapters/vercel.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;AAmGQ,UA1DS,iBAAA,CA0DT;EAAO,SAAA,QAAA,EAAA,MAAA;EAsBE,SAAA,OAAA,EAAA,MAAoB;EAWH,SAAA,oBAAA,CAAA,EAAA,MAAA,GAAA,MAAA;EAAR;;;AAoC1B;;EAEW,SAAA,sBAAA,CAAA,EAAA,SAAA,MAAA,EAAA;;;AA+VX;;;;;;;;;;;UAxciB,UAAA;;;;;;;;;UAUA,sBAAA;;WAEN;cACG,cAAc;;;;;;kBAMV;sBACI,SAAS;;yBAEN,cAAc;;;WAG5B;cACG,cAAc;;;;;;kBAMV;sBACI,SAAS;QACvB;;yBAEiB;;;;;qBAKJ,QAAQ;;;;;;gCAMG,SAAS;;;;;;;;UASxB,oBAAA;;;;;;;;;;;0BAWS,QAAQ;;;;;;8BAMJ;;;;;;;;;;;;;;;;;;iBA8Bd,aAAA,QACP,6BACE,uBACR;;;;;;;;iBA8Va,yBAAA,CAAA"}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { ProviderHttpError, ProviderStreamParseError } from "../errors/errors.js";
|
|
2
|
+
import { applyReasoningPolicy } from "../reasoning/apply-policy.js";
|
|
3
|
+
import { resolveReasoningRetention } from "../reasoning/retention.js";
|
|
4
|
+
import { inferReasoningContract } from "../reasoning/classify-contract.js";
|
|
5
|
+
|
|
6
|
+
//#region src/adapters/vercel.ts
|
|
7
|
+
const DEFAULT_CAPABILITIES = {
|
|
8
|
+
streaming: true,
|
|
9
|
+
toolCalling: true,
|
|
10
|
+
parallelToolCalls: true,
|
|
11
|
+
multimodal: true,
|
|
12
|
+
structuredOutput: true,
|
|
13
|
+
reasoning: true,
|
|
14
|
+
contextWindow: 2e5,
|
|
15
|
+
maxOutput: 16384
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Wrap a Vercel AI SDK language-model value in a Graphorin
|
|
19
|
+
* {@link Provider}. The adapter passes Graphorin `Message`s through
|
|
20
|
+
* directly — both formats use the same role + content discriminated
|
|
21
|
+
* shape — and translates the streaming chunks emitted by the AI SDK
|
|
22
|
+
* onto Graphorin `ProviderEvent`s.
|
|
23
|
+
*
|
|
24
|
+
* The adapter auto-detects the model's
|
|
25
|
+
* {@link import('@graphorin/core').ReasoningContract} from its
|
|
26
|
+
* `modelId` (e.g. Anthropic Claude → `'round-trip-required'`,
|
|
27
|
+
* OpenAI o1 / o3 → `'hidden'`, Gemini reasoning variants →
|
|
28
|
+
* `'hidden'`, everything else → `'optional'`). Callers can override
|
|
29
|
+
* the inferred value via `options.capabilities.reasoningContract`.
|
|
30
|
+
*
|
|
31
|
+
* @stable
|
|
32
|
+
*/
|
|
33
|
+
function vercelAdapter(model, options = {}) {
|
|
34
|
+
const name = options.name ?? `${model.provider}-${model.modelId}`;
|
|
35
|
+
const inferredContract = inferReasoningContract({
|
|
36
|
+
modelId: model.modelId,
|
|
37
|
+
provider: model.provider
|
|
38
|
+
});
|
|
39
|
+
const capabilities = {
|
|
40
|
+
...DEFAULT_CAPABILITIES,
|
|
41
|
+
reasoningContract: inferredContract,
|
|
42
|
+
...options.capabilities
|
|
43
|
+
};
|
|
44
|
+
const runtime = options.runtimeOverrides;
|
|
45
|
+
return {
|
|
46
|
+
name,
|
|
47
|
+
modelId: model.modelId,
|
|
48
|
+
capabilities,
|
|
49
|
+
stream(req) {
|
|
50
|
+
return streamFromVercel(model, name, capabilities, req, runtime);
|
|
51
|
+
},
|
|
52
|
+
async generate(req) {
|
|
53
|
+
return generateFromVercel(model, name, capabilities, req, runtime);
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async function* streamFromVercel(model, providerName, capabilities, req, overrides) {
|
|
58
|
+
const sdk = await loadRuntime(overrides);
|
|
59
|
+
const callArgs = buildCallArgs(model, applyRequestPreflight(req, capabilities));
|
|
60
|
+
let stream;
|
|
61
|
+
try {
|
|
62
|
+
stream = sdk.streamText(callArgs).fullStream;
|
|
63
|
+
} catch (cause) {
|
|
64
|
+
throw new ProviderHttpError({
|
|
65
|
+
providerName,
|
|
66
|
+
status: statusFromCause(cause),
|
|
67
|
+
message: "streamText() failed before yielding any chunks",
|
|
68
|
+
cause
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
yield {
|
|
72
|
+
type: "stream-start",
|
|
73
|
+
metadata: {
|
|
74
|
+
providerName,
|
|
75
|
+
modelId: model.modelId
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
let finalUsage;
|
|
79
|
+
let finishReason = "stop";
|
|
80
|
+
for await (const chunk of stream) {
|
|
81
|
+
if (req.signal?.aborted) {
|
|
82
|
+
finishReason = "aborted";
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
switch (chunk.type) {
|
|
86
|
+
case "text-delta": {
|
|
87
|
+
const delta = pickString(chunk.textDelta) ?? pickString(chunk.text) ?? "";
|
|
88
|
+
if (delta.length > 0) yield {
|
|
89
|
+
type: "text-delta",
|
|
90
|
+
delta
|
|
91
|
+
};
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case "reasoning":
|
|
95
|
+
case "reasoning-delta": {
|
|
96
|
+
const delta = pickString(chunk.textDelta) ?? pickString(chunk.delta) ?? pickString(chunk.text) ?? "";
|
|
97
|
+
if (delta.length > 0) yield {
|
|
98
|
+
type: "reasoning-delta",
|
|
99
|
+
delta
|
|
100
|
+
};
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
case "tool-call-streaming-start":
|
|
104
|
+
case "tool-input-start": {
|
|
105
|
+
const toolCallId = pickString(chunk.toolCallId) ?? pickString(chunk.id);
|
|
106
|
+
const toolName = pickString(chunk.toolName);
|
|
107
|
+
if (toolCallId !== void 0 && toolName !== void 0) yield {
|
|
108
|
+
type: "tool-call-start",
|
|
109
|
+
toolCallId,
|
|
110
|
+
toolName
|
|
111
|
+
};
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
case "tool-call-delta":
|
|
115
|
+
case "tool-input-delta": {
|
|
116
|
+
const toolCallId = pickString(chunk.toolCallId) ?? pickString(chunk.id);
|
|
117
|
+
const argsDelta = pickString(chunk.argsTextDelta) ?? pickString(chunk.delta) ?? pickString(chunk.inputTextDelta);
|
|
118
|
+
if (toolCallId !== void 0 && argsDelta !== void 0 && argsDelta.length > 0) yield {
|
|
119
|
+
type: "tool-call-input-delta",
|
|
120
|
+
toolCallId,
|
|
121
|
+
argsDelta
|
|
122
|
+
};
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
case "tool-call": {
|
|
126
|
+
const toolCallId = pickString(chunk.toolCallId) ?? pickString(chunk.id);
|
|
127
|
+
if (toolCallId !== void 0) yield {
|
|
128
|
+
type: "tool-call-end",
|
|
129
|
+
toolCallId,
|
|
130
|
+
finalArgs: chunk.args ?? chunk.input
|
|
131
|
+
};
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
case "finish":
|
|
135
|
+
finishReason = mapFinishReason(pickString(chunk.finishReason));
|
|
136
|
+
finalUsage = mapUsage(chunk.totalUsage ?? chunk.usage);
|
|
137
|
+
break;
|
|
138
|
+
case "error": {
|
|
139
|
+
const errorField = chunk.error;
|
|
140
|
+
yield {
|
|
141
|
+
type: "error",
|
|
142
|
+
error: {
|
|
143
|
+
kind: "unknown",
|
|
144
|
+
message: typeof errorField === "string" ? errorField : typeof errorField === "object" && errorField !== null ? pickString(errorField.message) ?? "unknown error" : "unknown error"
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
default: break;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
yield {
|
|
153
|
+
type: "finish",
|
|
154
|
+
finishReason,
|
|
155
|
+
usage: finalUsage ?? {
|
|
156
|
+
promptTokens: 0,
|
|
157
|
+
completionTokens: 0,
|
|
158
|
+
totalTokens: 0
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
async function generateFromVercel(model, providerName, capabilities, req, overrides) {
|
|
163
|
+
const sdk = await loadRuntime(overrides);
|
|
164
|
+
const callArgs = buildCallArgs(model, applyRequestPreflight(req, capabilities));
|
|
165
|
+
let result;
|
|
166
|
+
try {
|
|
167
|
+
result = await sdk.generateText(callArgs);
|
|
168
|
+
} catch (cause) {
|
|
169
|
+
throw new ProviderHttpError({
|
|
170
|
+
providerName,
|
|
171
|
+
status: statusFromCause(cause),
|
|
172
|
+
message: "generateText() rejected",
|
|
173
|
+
cause
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
usage: mapUsage(result.usage) ?? {
|
|
178
|
+
promptTokens: 0,
|
|
179
|
+
completionTokens: 0,
|
|
180
|
+
totalTokens: 0
|
|
181
|
+
},
|
|
182
|
+
finishReason: mapFinishReason(result.finishReason),
|
|
183
|
+
...result.text !== void 0 ? { text: result.text } : {},
|
|
184
|
+
...result.toolCalls !== void 0 ? { toolCalls: result.toolCalls.map(normalizeToolCall) } : {},
|
|
185
|
+
...result.providerMetadata !== void 0 ? { providerMetadata: result.providerMetadata } : {}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function applyRequestPreflight(req, capabilities) {
|
|
189
|
+
const retention = resolveReasoningRetention({
|
|
190
|
+
...req.reasoningRetention !== void 0 ? { requested: req.reasoningRetention } : {},
|
|
191
|
+
...capabilities.reasoningContract !== void 0 ? { contract: capabilities.reasoningContract } : {}
|
|
192
|
+
});
|
|
193
|
+
if (retention === "pass-through-all") return req;
|
|
194
|
+
const filtered = applyReasoningPolicy({
|
|
195
|
+
messages: req.messages,
|
|
196
|
+
retention
|
|
197
|
+
});
|
|
198
|
+
if (filtered === req.messages) return req;
|
|
199
|
+
return {
|
|
200
|
+
...req,
|
|
201
|
+
messages: filtered
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function buildCallArgs(model, req) {
|
|
205
|
+
return {
|
|
206
|
+
model,
|
|
207
|
+
messages: req.messages,
|
|
208
|
+
...req.systemMessage !== void 0 ? { system: req.systemMessage } : {},
|
|
209
|
+
...req.tools !== void 0 ? { tools: req.tools } : {},
|
|
210
|
+
...req.toolChoice !== void 0 ? { toolChoice: req.toolChoice } : {},
|
|
211
|
+
...req.temperature !== void 0 ? { temperature: req.temperature } : {},
|
|
212
|
+
...req.maxTokens !== void 0 ? {
|
|
213
|
+
maxTokens: req.maxTokens,
|
|
214
|
+
maxOutputTokens: req.maxTokens
|
|
215
|
+
} : {},
|
|
216
|
+
...req.signal !== void 0 ? { abortSignal: req.signal } : {},
|
|
217
|
+
...req.providerOptions !== void 0 ? { providerOptions: req.providerOptions } : {}
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function pickString(value) {
|
|
221
|
+
return typeof value === "string" ? value : void 0;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Normalize a generate() tool call across peers: AI SDK v4 carries
|
|
225
|
+
* `args`, v7 carries `input` (PS-6). The framework shape is `args`.
|
|
226
|
+
*/
|
|
227
|
+
function normalizeToolCall(tc) {
|
|
228
|
+
const t = tc;
|
|
229
|
+
return {
|
|
230
|
+
toolCallId: pickString(t.toolCallId) ?? pickString(t.id) ?? "",
|
|
231
|
+
toolName: pickString(t.toolName) ?? "",
|
|
232
|
+
args: t.args ?? t.input
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Lift a real HTTP status from a rejected AI SDK call (PS-2). The SDK's
|
|
237
|
+
* `APICallError` carries a numeric `statusCode`; surfacing it lets
|
|
238
|
+
* `withRetry` / `withFallback` see a genuine 429 / 5xx instead of the
|
|
239
|
+
* `status: 0` network-error placeholder. Returns `0` when no status is
|
|
240
|
+
* present (a true transport-level failure or an abort) — `0` is itself
|
|
241
|
+
* retryable / fallback-eligible by default, while an abort is excluded by
|
|
242
|
+
* the predicates via the wrapped `cause`.
|
|
243
|
+
*/
|
|
244
|
+
function statusFromCause(cause) {
|
|
245
|
+
if (cause !== null && typeof cause === "object") {
|
|
246
|
+
const c = cause;
|
|
247
|
+
if (typeof c.statusCode === "number" && Number.isFinite(c.statusCode)) return c.statusCode;
|
|
248
|
+
if (typeof c.status === "number" && Number.isFinite(c.status)) return c.status;
|
|
249
|
+
}
|
|
250
|
+
return 0;
|
|
251
|
+
}
|
|
252
|
+
function mapFinishReason(value) {
|
|
253
|
+
switch (value) {
|
|
254
|
+
case "stop":
|
|
255
|
+
case "length":
|
|
256
|
+
case "tool-calls":
|
|
257
|
+
case "content-filter":
|
|
258
|
+
case "error": return value;
|
|
259
|
+
case "aborted":
|
|
260
|
+
case "cancelled": return "aborted";
|
|
261
|
+
default: return "stop";
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function mapUsage(input) {
|
|
265
|
+
if (input === void 0 || input === null || typeof input !== "object") return void 0;
|
|
266
|
+
const u = input;
|
|
267
|
+
const promptTokens = u.promptTokens ?? u.inputTokens ?? 0;
|
|
268
|
+
const completionTokens = u.completionTokens ?? u.outputTokens ?? 0;
|
|
269
|
+
return {
|
|
270
|
+
promptTokens,
|
|
271
|
+
completionTokens,
|
|
272
|
+
totalTokens: u.totalTokens ?? promptTokens + completionTokens,
|
|
273
|
+
...u.reasoningTokens !== void 0 ? { reasoningTokens: u.reasoningTokens } : {}
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
let cachedRuntime = null;
|
|
277
|
+
async function loadRuntime(overrides) {
|
|
278
|
+
if (overrides !== void 0) return overrides;
|
|
279
|
+
if (cachedRuntime !== null) return cachedRuntime;
|
|
280
|
+
let mod;
|
|
281
|
+
try {
|
|
282
|
+
mod = await import("ai");
|
|
283
|
+
} catch (cause) {
|
|
284
|
+
throw new ProviderStreamParseError("vercel", "Failed to import the 'ai' peer dependency. Install it with `pnpm add ai` or pass a runtimeOverrides value.", cause);
|
|
285
|
+
}
|
|
286
|
+
if (typeof mod.streamText !== "function" || typeof mod.generateText !== "function") throw new ProviderStreamParseError("vercel", "The installed 'ai' package does not expose streamText / generateText functions.");
|
|
287
|
+
cachedRuntime = {
|
|
288
|
+
streamText: mod.streamText,
|
|
289
|
+
generateText: mod.generateText
|
|
290
|
+
};
|
|
291
|
+
return cachedRuntime;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Test-only hook that resets the cached AI SDK runtime. Provider tests
|
|
295
|
+
* that mutate the cache (e.g. by injecting a mock then verifying the
|
|
296
|
+
* default loader runs) call this between scenarios.
|
|
297
|
+
*
|
|
298
|
+
* @internal
|
|
299
|
+
*/
|
|
300
|
+
function __resetVercelRuntimeCache() {
|
|
301
|
+
cachedRuntime = null;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
//#endregion
|
|
305
|
+
export { __resetVercelRuntimeCache, vercelAdapter };
|
|
306
|
+
//# sourceMappingURL=vercel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vercel.js","names":["DEFAULT_CAPABILITIES: Omit<ProviderCapabilities, 'reasoningContract'>","capabilities: ProviderCapabilities","stream: AsyncIterable<AISDKChunk>","finalUsage: Usage | undefined","finishReason: FinishReason","result: Awaited<ReturnType<VercelRuntimeOverrides['generateText']>>","cachedRuntime: VercelRuntimeOverrides | null","mod: { streamText?: unknown; generateText?: unknown }"],"sources":["../../src/adapters/vercel.ts"],"sourcesContent":["/**\n * `vercelAdapter` — wraps a Vercel AI SDK `LanguageModel`-shaped value\n * into a Graphorin {@link Provider}. The adapter is the default cloud\n * path: it speaks the AI SDK's `streamText` / `generateText` API and\n * maps the resulting events onto the canonical\n * {@link import('@graphorin/core').ProviderEvent} discriminated union.\n *\n * The AI SDK is an **optional peer dependency** of `@graphorin/provider`.\n * Production callers leave `runtimeOverrides` unset and the adapter\n * dynamically imports the package on first use; test fixtures pass a\n * `runtimeOverrides` value to short-circuit the import and feed\n * fixture chunks directly. The overrides shape is intentionally\n * structural so users can supply hand-rolled stubs or any compatible\n * library.\n *\n * @packageDocumentation\n */\n\nimport type {\n FinishReason,\n Message,\n Provider,\n ProviderCapabilities,\n ProviderEvent,\n ProviderRequest,\n ProviderResponse,\n Usage,\n} from '@graphorin/core';\n\nimport { ProviderHttpError, ProviderStreamParseError } from '../errors/errors.js';\nimport { applyReasoningPolicy } from '../reasoning/apply-policy.js';\nimport { inferReasoningContract } from '../reasoning/classify-contract.js';\nimport { resolveReasoningRetention } from '../reasoning/retention.js';\n\n/**\n * Structural shape the adapter expects from the AI SDK language model\n * value. The real `LanguageModelV4` matches this shape. Re-declared\n * here so we do not pin a hard dependency on `@ai-sdk/provider`.\n *\n * @stable\n */\nexport interface LanguageModelLike {\n readonly provider: string;\n readonly modelId: string;\n readonly specificationVersion?: string | number;\n /**\n * Optional capability flags carried by the AI SDK model. The adapter\n * forwards them onto the canonical `ProviderCapabilities` shape;\n * missing values are filled in with conservative defaults.\n */\n readonly supportedToolCallTypes?: readonly string[];\n}\n\n/**\n * Loose chunk shape emitted by the AI SDK's `streamText`. The shape is\n * intentionally permissive — we accept anything that carries the\n * fields we use and ignore the rest. This keeps the adapter tolerant\n * of additive AI SDK schema changes.\n *\n * The fields we read are normalized in the adapter via narrow helper\n * functions, so we deliberately type each as `unknown` and gate\n * access behind `typeof` checks at runtime.\n *\n * @stable\n */\nexport interface AISDKChunk {\n readonly type: string;\n readonly [extra: string]: unknown;\n}\n\n/**\n * Subset of the AI SDK surface used by the adapter.\n *\n * @stable\n */\nexport interface VercelRuntimeOverrides {\n readonly streamText: (args: {\n model: LanguageModelLike;\n messages: ReadonlyArray<Message>;\n system?: string;\n tools?: unknown;\n toolChoice?: unknown;\n temperature?: number;\n maxTokens?: number;\n abortSignal?: AbortSignal;\n providerOptions?: Readonly<Record<string, unknown>>;\n }) => {\n readonly fullStream: AsyncIterable<AISDKChunk>;\n };\n readonly generateText: (args: {\n model: LanguageModelLike;\n messages: ReadonlyArray<Message>;\n system?: string;\n tools?: unknown;\n toolChoice?: unknown;\n temperature?: number;\n maxTokens?: number;\n abortSignal?: AbortSignal;\n providerOptions?: Readonly<Record<string, unknown>>;\n }) => Promise<{\n readonly text?: string;\n readonly toolCalls?: ReadonlyArray<{\n readonly toolCallId: string;\n readonly toolName: string;\n readonly args: unknown;\n }>;\n readonly usage?: Partial<Usage> & {\n readonly inputTokens?: number;\n readonly outputTokens?: number;\n readonly totalTokens?: number;\n };\n readonly finishReason?: string;\n readonly providerMetadata?: Readonly<Record<string, unknown>>;\n }>;\n}\n\n/**\n * Options accepted by {@link vercelAdapter}.\n *\n * @stable\n */\nexport interface VercelAdapterOptions {\n /**\n * Fully-qualified provider name, used for span / log labelling.\n * Defaults to `${model.provider}-${model.modelId}`.\n */\n readonly name?: string;\n /**\n * Capability declaration. The adapter merges these on top of a\n * conservative defaults table (`streaming: true`, `toolCalling: true`,\n * `multimodal: true`, …); supply explicit values to narrow them.\n */\n readonly capabilities?: Partial<ProviderCapabilities>;\n /**\n * Runtime override for the AI SDK functions. When unset, the adapter\n * lazily `await import('ai')` on first call. Test suites pass a\n * fixture-driven implementation directly.\n */\n readonly runtimeOverrides?: VercelRuntimeOverrides;\n}\n\nconst DEFAULT_CAPABILITIES: Omit<ProviderCapabilities, 'reasoningContract'> = {\n streaming: true,\n toolCalling: true,\n parallelToolCalls: true,\n multimodal: true,\n structuredOutput: true,\n reasoning: true,\n contextWindow: 200_000,\n maxOutput: 16_384,\n};\n\n/**\n * Wrap a Vercel AI SDK language-model value in a Graphorin\n * {@link Provider}. The adapter passes Graphorin `Message`s through\n * directly — both formats use the same role + content discriminated\n * shape — and translates the streaming chunks emitted by the AI SDK\n * onto Graphorin `ProviderEvent`s.\n *\n * The adapter auto-detects the model's\n * {@link import('@graphorin/core').ReasoningContract} from its\n * `modelId` (e.g. Anthropic Claude → `'round-trip-required'`,\n * OpenAI o1 / o3 → `'hidden'`, Gemini reasoning variants →\n * `'hidden'`, everything else → `'optional'`). Callers can override\n * the inferred value via `options.capabilities.reasoningContract`.\n *\n * @stable\n */\nexport function vercelAdapter(\n model: LanguageModelLike,\n options: VercelAdapterOptions = {},\n): Provider {\n const name = options.name ?? `${model.provider}-${model.modelId}`;\n const inferredContract = inferReasoningContract({\n modelId: model.modelId,\n provider: model.provider,\n });\n const capabilities: ProviderCapabilities = {\n ...DEFAULT_CAPABILITIES,\n reasoningContract: inferredContract,\n ...options.capabilities,\n };\n const runtime = options.runtimeOverrides;\n\n return {\n name,\n modelId: model.modelId,\n capabilities,\n stream(req) {\n return streamFromVercel(model, name, capabilities, req, runtime);\n },\n async generate(req) {\n return generateFromVercel(model, name, capabilities, req, runtime);\n },\n };\n}\n\nasync function* streamFromVercel(\n model: LanguageModelLike,\n providerName: string,\n capabilities: ProviderCapabilities,\n req: ProviderRequest,\n overrides: VercelRuntimeOverrides | undefined,\n): AsyncIterable<ProviderEvent> {\n const sdk = await loadRuntime(overrides);\n const callArgs = buildCallArgs(model, applyRequestPreflight(req, capabilities));\n let stream: AsyncIterable<AISDKChunk>;\n try {\n const result = sdk.streamText(callArgs);\n stream = result.fullStream;\n } catch (cause) {\n throw new ProviderHttpError({\n providerName,\n status: statusFromCause(cause),\n message: 'streamText() failed before yielding any chunks',\n cause,\n });\n }\n\n yield {\n type: 'stream-start',\n metadata: {\n providerName,\n modelId: model.modelId,\n },\n };\n\n let finalUsage: Usage | undefined;\n let finishReason: FinishReason = 'stop';\n\n for await (const chunk of stream) {\n if (req.signal?.aborted) {\n // PS-12: an aborted stream must report 'aborted', not the initial 'stop'\n // — mirrors the openai-shaped and ollama adapters.\n finishReason = 'aborted';\n break;\n }\n switch (chunk.type) {\n case 'text-delta': {\n const delta = pickString(chunk.textDelta) ?? pickString(chunk.text) ?? '';\n if (delta.length > 0) {\n yield { type: 'text-delta', delta };\n }\n break;\n }\n case 'reasoning':\n case 'reasoning-delta': {\n const delta =\n pickString(chunk.textDelta) ?? pickString(chunk.delta) ?? pickString(chunk.text) ?? '';\n if (delta.length > 0) {\n yield { type: 'reasoning-delta', delta };\n }\n break;\n }\n // PS-6 dual-shape: AI SDK v4 streams `tool-call-streaming-start` /\n // `tool-call-delta` keyed by `toolCallId`/`argsTextDelta`; v7 streams\n // `tool-input-start` / `tool-input-delta` keyed by `id`/`delta`.\n case 'tool-call-streaming-start':\n case 'tool-input-start': {\n const toolCallId = pickString(chunk.toolCallId) ?? pickString(chunk.id);\n const toolName = pickString(chunk.toolName);\n if (toolCallId !== undefined && toolName !== undefined) {\n yield {\n type: 'tool-call-start',\n toolCallId,\n toolName,\n };\n }\n break;\n }\n case 'tool-call-delta':\n case 'tool-input-delta': {\n const toolCallId = pickString(chunk.toolCallId) ?? pickString(chunk.id);\n const argsDelta =\n pickString(chunk.argsTextDelta) ??\n pickString(chunk.delta) ??\n pickString(chunk.inputTextDelta);\n if (toolCallId !== undefined && argsDelta !== undefined && argsDelta.length > 0) {\n yield {\n type: 'tool-call-input-delta',\n toolCallId,\n argsDelta,\n };\n }\n break;\n }\n case 'tool-call': {\n const toolCallId = pickString(chunk.toolCallId) ?? pickString(chunk.id);\n if (toolCallId !== undefined) {\n yield {\n type: 'tool-call-end',\n toolCallId,\n // v4 carries `args`; v7 carries `input`.\n finalArgs: chunk.args ?? chunk.input,\n };\n }\n break;\n }\n case 'finish': {\n finishReason = mapFinishReason(pickString(chunk.finishReason));\n // v4 carries `usage`; v7 carries `totalUsage` (zeroing the v4\n // read nulled cost tracking on streaming).\n finalUsage = mapUsage(chunk.totalUsage ?? chunk.usage);\n break;\n }\n case 'error': {\n const errorField = chunk.error;\n const message =\n typeof errorField === 'string'\n ? errorField\n : typeof errorField === 'object' && errorField !== null\n ? (pickString((errorField as { message?: unknown }).message) ?? 'unknown error')\n : 'unknown error';\n yield {\n type: 'error',\n error: { kind: 'unknown', message },\n };\n break;\n }\n default:\n // Unknown chunk types are forward-compatible no-ops; we keep\n // streaming so additive AI SDK upgrades do not break callers.\n break;\n }\n }\n\n yield {\n type: 'finish',\n finishReason,\n usage: finalUsage ?? { promptTokens: 0, completionTokens: 0, totalTokens: 0 },\n };\n}\n\nasync function generateFromVercel(\n model: LanguageModelLike,\n providerName: string,\n capabilities: ProviderCapabilities,\n req: ProviderRequest,\n overrides: VercelRuntimeOverrides | undefined,\n): Promise<ProviderResponse> {\n const sdk = await loadRuntime(overrides);\n const callArgs = buildCallArgs(model, applyRequestPreflight(req, capabilities));\n let result: Awaited<ReturnType<VercelRuntimeOverrides['generateText']>>;\n try {\n result = await sdk.generateText(callArgs);\n } catch (cause) {\n throw new ProviderHttpError({\n providerName,\n status: statusFromCause(cause),\n message: 'generateText() rejected',\n cause,\n });\n }\n const usage = mapUsage(result.usage) ?? {\n promptTokens: 0,\n completionTokens: 0,\n totalTokens: 0,\n };\n const finishReason = mapFinishReason(result.finishReason);\n const response: ProviderResponse = {\n usage,\n finishReason,\n ...(result.text !== undefined ? { text: result.text } : {}),\n ...(result.toolCalls !== undefined\n ? { toolCalls: result.toolCalls.map(normalizeToolCall) }\n : {}),\n ...(result.providerMetadata !== undefined ? { providerMetadata: result.providerMetadata } : {}),\n };\n return response;\n}\n\nfunction applyRequestPreflight(\n req: ProviderRequest,\n capabilities: ProviderCapabilities,\n): ProviderRequest {\n const retention = resolveReasoningRetention({\n ...(req.reasoningRetention !== undefined ? { requested: req.reasoningRetention } : {}),\n ...(capabilities.reasoningContract !== undefined\n ? { contract: capabilities.reasoningContract }\n : {}),\n });\n if (retention === 'pass-through-all') return req;\n const filtered = applyReasoningPolicy({ messages: req.messages, retention });\n if (filtered === req.messages) return req;\n return { ...req, messages: filtered };\n}\n\nfunction buildCallArgs(model: LanguageModelLike, req: ProviderRequest) {\n return {\n model,\n messages: req.messages,\n ...(req.systemMessage !== undefined ? { system: req.systemMessage } : {}),\n ...(req.tools !== undefined ? { tools: req.tools } : {}),\n ...(req.toolChoice !== undefined ? { toolChoice: req.toolChoice } : {}),\n ...(req.temperature !== undefined ? { temperature: req.temperature } : {}),\n // v4 reads `maxTokens`; v7 renamed it `maxOutputTokens` — send both\n // so the cap is honoured against either peer (PS-6).\n ...(req.maxTokens !== undefined\n ? { maxTokens: req.maxTokens, maxOutputTokens: req.maxTokens }\n : {}),\n ...(req.signal !== undefined ? { abortSignal: req.signal } : {}),\n ...(req.providerOptions !== undefined ? { providerOptions: req.providerOptions } : {}),\n } as const;\n}\n\nfunction pickString(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\n/**\n * Normalize a generate() tool call across peers: AI SDK v4 carries\n * `args`, v7 carries `input` (PS-6). The framework shape is `args`.\n */\nfunction normalizeToolCall(tc: unknown): { toolCallId: string; toolName: string; args: unknown } {\n const t = tc as {\n toolCallId?: unknown;\n id?: unknown;\n toolName?: unknown;\n args?: unknown;\n input?: unknown;\n };\n return {\n toolCallId: pickString(t.toolCallId) ?? pickString(t.id) ?? '',\n toolName: pickString(t.toolName) ?? '',\n args: t.args ?? t.input,\n };\n}\n\n/**\n * Lift a real HTTP status from a rejected AI SDK call (PS-2). The SDK's\n * `APICallError` carries a numeric `statusCode`; surfacing it lets\n * `withRetry` / `withFallback` see a genuine 429 / 5xx instead of the\n * `status: 0` network-error placeholder. Returns `0` when no status is\n * present (a true transport-level failure or an abort) — `0` is itself\n * retryable / fallback-eligible by default, while an abort is excluded by\n * the predicates via the wrapped `cause`.\n */\nfunction statusFromCause(cause: unknown): number {\n if (cause !== null && typeof cause === 'object') {\n const c = cause as { statusCode?: unknown; status?: unknown };\n if (typeof c.statusCode === 'number' && Number.isFinite(c.statusCode)) return c.statusCode;\n if (typeof c.status === 'number' && Number.isFinite(c.status)) return c.status;\n }\n return 0;\n}\n\nfunction mapFinishReason(value: string | undefined): FinishReason {\n switch (value) {\n case 'stop':\n case 'length':\n case 'tool-calls':\n case 'content-filter':\n case 'error':\n return value;\n case 'aborted':\n case 'cancelled':\n return 'aborted';\n default:\n return 'stop';\n }\n}\n\nfunction mapUsage(input: unknown): Usage | undefined {\n if (input === undefined || input === null || typeof input !== 'object') return undefined;\n const u = input as {\n promptTokens?: number;\n completionTokens?: number;\n inputTokens?: number;\n outputTokens?: number;\n reasoningTokens?: number;\n totalTokens?: number;\n };\n const promptTokens = u.promptTokens ?? u.inputTokens ?? 0;\n const completionTokens = u.completionTokens ?? u.outputTokens ?? 0;\n const totalTokens = u.totalTokens ?? promptTokens + completionTokens;\n const usage: Usage = {\n promptTokens,\n completionTokens,\n totalTokens,\n ...(u.reasoningTokens !== undefined ? { reasoningTokens: u.reasoningTokens } : {}),\n };\n return usage;\n}\n\nlet cachedRuntime: VercelRuntimeOverrides | null = null;\n\nasync function loadRuntime(\n overrides: VercelRuntimeOverrides | undefined,\n): Promise<VercelRuntimeOverrides> {\n if (overrides !== undefined) return overrides;\n if (cachedRuntime !== null) return cachedRuntime;\n let mod: { streamText?: unknown; generateText?: unknown };\n try {\n mod = (await import('ai')) as { streamText?: unknown; generateText?: unknown };\n } catch (cause) {\n throw new ProviderStreamParseError(\n 'vercel',\n \"Failed to import the 'ai' peer dependency. Install it with `pnpm add ai` or pass a runtimeOverrides value.\",\n cause,\n );\n }\n if (typeof mod.streamText !== 'function' || typeof mod.generateText !== 'function') {\n throw new ProviderStreamParseError(\n 'vercel',\n \"The installed 'ai' package does not expose streamText / generateText functions.\",\n );\n }\n cachedRuntime = {\n streamText: mod.streamText as VercelRuntimeOverrides['streamText'],\n generateText: mod.generateText as VercelRuntimeOverrides['generateText'],\n };\n return cachedRuntime;\n}\n\n/**\n * Test-only hook that resets the cached AI SDK runtime. Provider tests\n * that mutate the cache (e.g. by injecting a mock then verifying the\n * default loader runs) call this between scenarios.\n *\n * @internal\n */\nexport function __resetVercelRuntimeCache(): void {\n cachedRuntime = null;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Message, TokenCounter } from "@graphorin/core";
|
|
2
|
+
|
|
3
|
+
//#region src/counters/anthropic.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Options for {@link AnthropicAPICounter}.
|
|
7
|
+
*
|
|
8
|
+
* @stable
|
|
9
|
+
*/
|
|
10
|
+
interface AnthropicAPICounterOptions {
|
|
11
|
+
readonly modelId: string;
|
|
12
|
+
readonly apiKey?: string;
|
|
13
|
+
readonly baseUrl?: string;
|
|
14
|
+
readonly fetchImpl?: typeof fetch;
|
|
15
|
+
/** Identifier carried on the produced counter. */
|
|
16
|
+
readonly id?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Counter that talks to `POST /v1/messages/count_tokens` when an API
|
|
20
|
+
* key is configured. Without an API key, the counter delegates to
|
|
21
|
+
* `JsTiktokenCounter('cl100k_base')` — the closest publicly-available
|
|
22
|
+
* proxy for Anthropic's tokenizer.
|
|
23
|
+
*
|
|
24
|
+
* @stable
|
|
25
|
+
*/
|
|
26
|
+
declare class AnthropicAPICounter implements TokenCounter {
|
|
27
|
+
#private;
|
|
28
|
+
readonly id: string;
|
|
29
|
+
readonly version: string;
|
|
30
|
+
constructor(options: AnthropicAPICounterOptions);
|
|
31
|
+
count(messages: ReadonlyArray<Message>): Promise<number>;
|
|
32
|
+
countText(text: string): Promise<number>;
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { AnthropicAPICounter, AnthropicAPICounterOptions };
|
|
36
|
+
//# sourceMappingURL=anthropic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.d.ts","names":[],"sources":["../../src/counters/anthropic.ts"],"sourcesContent":[],"mappings":";;;;;;;;;UAoBiB,0BAAA;;;;8BAIa;;;;;;;;;;;;cAajB,mBAAA,YAA+B;;;;uBASrB;kBAUC,cAAc,WAAW;2BAqBhB"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { stripTrailingSlashes } from "../internal/url-utils.js";
|
|
2
|
+
import { JsTiktokenCounter } from "./js-tiktoken.js";
|
|
3
|
+
|
|
4
|
+
//#region src/counters/anthropic.ts
|
|
5
|
+
/**
|
|
6
|
+
* Counter that talks to `POST /v1/messages/count_tokens` when an API
|
|
7
|
+
* key is configured. Without an API key, the counter delegates to
|
|
8
|
+
* `JsTiktokenCounter('cl100k_base')` — the closest publicly-available
|
|
9
|
+
* proxy for Anthropic's tokenizer.
|
|
10
|
+
*
|
|
11
|
+
* @stable
|
|
12
|
+
*/
|
|
13
|
+
var AnthropicAPICounter = class {
|
|
14
|
+
id;
|
|
15
|
+
version;
|
|
16
|
+
#modelId;
|
|
17
|
+
#apiKey;
|
|
18
|
+
#baseUrl;
|
|
19
|
+
#fetchImpl;
|
|
20
|
+
#fallback;
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.#modelId = options.modelId;
|
|
23
|
+
if (options.apiKey !== void 0) this.#apiKey = options.apiKey;
|
|
24
|
+
this.#baseUrl = stripTrailingSlashes(options.baseUrl ?? "https://api.anthropic.com");
|
|
25
|
+
this.#fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
26
|
+
this.#fallback = new JsTiktokenCounter({
|
|
27
|
+
encoding: "cl100k_base",
|
|
28
|
+
modelId: options.modelId
|
|
29
|
+
});
|
|
30
|
+
this.id = options.id ?? `anthropic-native@${options.modelId}`;
|
|
31
|
+
this.version = `anthropic-native-${options.modelId}-v1`;
|
|
32
|
+
}
|
|
33
|
+
async count(messages) {
|
|
34
|
+
if (this.#apiKey === void 0) return this.#fallback.count(messages);
|
|
35
|
+
try {
|
|
36
|
+
const resp = await this.#fetchImpl(`${this.#baseUrl}/v1/messages/count_tokens`, {
|
|
37
|
+
method: "POST",
|
|
38
|
+
headers: {
|
|
39
|
+
"content-type": "application/json",
|
|
40
|
+
"x-api-key": this.#apiKey,
|
|
41
|
+
"anthropic-version": "2023-06-01"
|
|
42
|
+
},
|
|
43
|
+
body: JSON.stringify({
|
|
44
|
+
model: this.#modelId,
|
|
45
|
+
messages
|
|
46
|
+
})
|
|
47
|
+
});
|
|
48
|
+
if (!resp.ok) return this.#fallback.count(messages);
|
|
49
|
+
const json = await resp.json();
|
|
50
|
+
if (typeof json.input_tokens === "number") return json.input_tokens;
|
|
51
|
+
return this.#fallback.count(messages);
|
|
52
|
+
} catch {
|
|
53
|
+
return this.#fallback.count(messages);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async countText(text) {
|
|
57
|
+
return this.#fallback.countText(text);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
//#endregion
|
|
62
|
+
export { AnthropicAPICounter };
|
|
63
|
+
//# sourceMappingURL=anthropic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.js","names":["#modelId","#apiKey","#baseUrl","#fetchImpl","#fallback"],"sources":["../../src/counters/anthropic.ts"],"sourcesContent":["/**\n * `AnthropicAPICounter` — uses Anthropic's native count-tokens\n * endpoint when an API key is configured, otherwise falls back to\n * `JsTiktokenCounter`. The native call is opt-in: the user supplies\n * an explicit `apiKey`. The framework never makes the call without\n * one.\n *\n * @packageDocumentation\n */\n\nimport type { Message, TokenCounter } from '@graphorin/core';\n\nimport { stripTrailingSlashes } from '../internal/url-utils.js';\nimport { JsTiktokenCounter } from './js-tiktoken.js';\n\n/**\n * Options for {@link AnthropicAPICounter}.\n *\n * @stable\n */\nexport interface AnthropicAPICounterOptions {\n readonly modelId: string;\n readonly apiKey?: string;\n readonly baseUrl?: string;\n readonly fetchImpl?: typeof fetch;\n /** Identifier carried on the produced counter. */\n readonly id?: string;\n}\n\n/**\n * Counter that talks to `POST /v1/messages/count_tokens` when an API\n * key is configured. Without an API key, the counter delegates to\n * `JsTiktokenCounter('cl100k_base')` — the closest publicly-available\n * proxy for Anthropic's tokenizer.\n *\n * @stable\n */\nexport class AnthropicAPICounter implements TokenCounter {\n readonly id: string;\n readonly version: string;\n readonly #modelId: string;\n readonly #apiKey?: string;\n readonly #baseUrl: string;\n readonly #fetchImpl: typeof fetch;\n readonly #fallback: JsTiktokenCounter;\n\n constructor(options: AnthropicAPICounterOptions) {\n this.#modelId = options.modelId;\n if (options.apiKey !== undefined) this.#apiKey = options.apiKey;\n this.#baseUrl = stripTrailingSlashes(options.baseUrl ?? 'https://api.anthropic.com');\n this.#fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);\n this.#fallback = new JsTiktokenCounter({ encoding: 'cl100k_base', modelId: options.modelId });\n this.id = options.id ?? `anthropic-native@${options.modelId}`;\n this.version = `anthropic-native-${options.modelId}-v1`;\n }\n\n async count(messages: ReadonlyArray<Message>): Promise<number> {\n if (this.#apiKey === undefined) return this.#fallback.count(messages);\n try {\n const resp = await this.#fetchImpl(`${this.#baseUrl}/v1/messages/count_tokens`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-api-key': this.#apiKey,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify({ model: this.#modelId, messages }),\n });\n if (!resp.ok) return this.#fallback.count(messages);\n const json = (await resp.json()) as { input_tokens?: number };\n if (typeof json.input_tokens === 'number') return json.input_tokens;\n return this.#fallback.count(messages);\n } catch {\n return this.#fallback.count(messages);\n }\n }\n\n async countText(text: string): Promise<number> {\n return this.#fallback.countText(text);\n }\n}\n"],"mappings":";;;;;;;;;;;;AAqCA,IAAa,sBAAb,MAAyD;CACvD,AAAS;CACT,AAAS;CACT,CAASA;CACT,CAASC;CACT,CAASC;CACT,CAASC;CACT,CAASC;CAET,YAAY,SAAqC;AAC/C,QAAKJ,UAAW,QAAQ;AACxB,MAAI,QAAQ,WAAW,OAAW,OAAKC,SAAU,QAAQ;AACzD,QAAKC,UAAW,qBAAqB,QAAQ,WAAW,4BAA4B;AACpF,QAAKC,YAAa,QAAQ,aAAa,WAAW,MAAM,KAAK,WAAW;AACxE,QAAKC,WAAY,IAAI,kBAAkB;GAAE,UAAU;GAAe,SAAS,QAAQ;GAAS,CAAC;AAC7F,OAAK,KAAK,QAAQ,MAAM,oBAAoB,QAAQ;AACpD,OAAK,UAAU,oBAAoB,QAAQ,QAAQ;;CAGrD,MAAM,MAAM,UAAmD;AAC7D,MAAI,MAAKH,WAAY,OAAW,QAAO,MAAKG,SAAU,MAAM,SAAS;AACrE,MAAI;GACF,MAAM,OAAO,MAAM,MAAKD,UAAW,GAAG,MAAKD,QAAS,4BAA4B;IAC9E,QAAQ;IACR,SAAS;KACP,gBAAgB;KAChB,aAAa,MAAKD;KAClB,qBAAqB;KACtB;IACD,MAAM,KAAK,UAAU;KAAE,OAAO,MAAKD;KAAU;KAAU,CAAC;IACzD,CAAC;AACF,OAAI,CAAC,KAAK,GAAI,QAAO,MAAKI,SAAU,MAAM,SAAS;GACnD,MAAM,OAAQ,MAAM,KAAK,MAAM;AAC/B,OAAI,OAAO,KAAK,iBAAiB,SAAU,QAAO,KAAK;AACvD,UAAO,MAAKA,SAAU,MAAM,SAAS;UAC/B;AACN,UAAO,MAAKA,SAAU,MAAM,SAAS;;;CAIzC,MAAM,UAAU,MAA+B;AAC7C,SAAO,MAAKA,SAAU,UAAU,KAAK"}
|