@merkie/agentic 0.1.0 → 0.1.1
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 +5 -6
- package/dist/index.cjs +1 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -11
- package/dist/index.d.ts +9 -11
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ Works in TypeScript and JavaScript, ESM and CommonJS.
|
|
|
35
35
|
import { streamText } from "ai";
|
|
36
36
|
import { createOpenRouter, logStream } from "@merkie/agentic";
|
|
37
37
|
|
|
38
|
-
//
|
|
38
|
+
// Behaves exactly like the upstream factory, just with usage tracking on.
|
|
39
39
|
const openrouter = createOpenRouter();
|
|
40
40
|
|
|
41
41
|
const result = streamText({
|
|
@@ -56,20 +56,19 @@ const { createOpenRouter, logStream } = require("@merkie/agentic");
|
|
|
56
56
|
|
|
57
57
|
### `createOpenRouter(settings?)`
|
|
58
58
|
|
|
59
|
-
Same signature
|
|
60
|
-
`@openrouter/ai-sdk-provider
|
|
59
|
+
Same signature, return type, and defaults as `createOpenRouter` from
|
|
60
|
+
`@openrouter/ai-sdk-provider` (including reading `OPENROUTER_API_KEY` from the
|
|
61
|
+
environment). The **only** thing added:
|
|
61
62
|
|
|
62
63
|
| Default | Behavior | Override |
|
|
63
64
|
| --- | --- | --- |
|
|
64
|
-
| `apiKey` | Falls back to `process.env.OPENROUTER_API_KEY` | Pass `apiKey` |
|
|
65
65
|
| `extraBody.usage.include` | `true` (returns cost + token usage) | Pass `extraBody.usage` |
|
|
66
66
|
|
|
67
|
-
Every option you pass wins over
|
|
67
|
+
Every option you pass wins over that default, so it's 1:1 compatible with the
|
|
68
68
|
upstream factory:
|
|
69
69
|
|
|
70
70
|
```ts
|
|
71
71
|
const openrouter = createOpenRouter({
|
|
72
|
-
apiKey: myKey,
|
|
73
72
|
extraBody: {
|
|
74
73
|
transforms: ["middle-out"],
|
|
75
74
|
usage: { include: false }, // opt back out if you want
|
package/dist/index.cjs
CHANGED
|
@@ -38,10 +38,9 @@ module.exports = __toCommonJS(index_exports);
|
|
|
38
38
|
// src/openrouter.ts
|
|
39
39
|
var import_ai_sdk_provider = require("@openrouter/ai-sdk-provider");
|
|
40
40
|
function createOpenRouter(settings = {}) {
|
|
41
|
-
const {
|
|
41
|
+
const { extraBody, ...rest } = settings;
|
|
42
42
|
const userUsage = extraBody && typeof extraBody === "object" && "usage" in extraBody ? extraBody.usage : void 0;
|
|
43
43
|
return (0, import_ai_sdk_provider.createOpenRouter)({
|
|
44
|
-
apiKey: apiKey ?? process.env.OPENROUTER_API_KEY,
|
|
45
44
|
...rest,
|
|
46
45
|
extraBody: {
|
|
47
46
|
...extraBody,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/openrouter.ts","../src/logStream.ts"],"sourcesContent":["export {\n\tcreateOpenRouter,\n\ttype OpenRouterProvider,\n\ttype OpenRouterProviderSettings,\n} from \"./openrouter.js\";\nexport { logStream } from \"./logStream.js\";\n","import {\n\tcreateOpenRouter as baseCreateOpenRouter,\n\ttype OpenRouterProvider,\n\ttype OpenRouterProviderSettings,\n} from \"@openrouter/ai-sdk-provider\";\n\n/**\n * Drop-in replacement for `createOpenRouter` from `@openrouter/ai-sdk-provider`.\n *\n * It behaves identically to the original, with two conveniences baked in:\n *\n * 1. `apiKey` defaults to `process.env.OPENROUTER_API_KEY` when you don't pass\n * one, so the common case is just `createOpenRouter()`.\n * 2. `extraBody.usage.include` defaults to `true`, which tells OpenRouter to\n * return cost + token accounting on every response. This is what powers the\n * mid-run cost/usage tracking in {@link logStream}.\n *\n * Every option you pass through wins over the defaults — including `usage` — so\n * this is 1:1 compatible with the upstream `createOpenRouter`. You can override\n * the api key, add your own `extraBody`, flip `usage.include` off, etc.\n *\n * @example\n * ```ts\n * import { createOpenRouter } from \"@merkie/agentic\";\n *\n * // Reads OPENROUTER_API_KEY from the environment, usage tracking on.\n * const openrouter = createOpenRouter();\n *\n * const model = openrouter(\"openai/gpt-4o-mini\");\n * ```\n */\nexport function createOpenRouter(\n\tsettings: OpenRouterProviderSettings = {},\n): OpenRouterProvider {\n\tconst { apiKey, extraBody, ...rest } = settings;\n\n\tconst userUsage =\n\t\textraBody && typeof extraBody === \"object\" && \"usage\" in extraBody\n\t\t\t? (extraBody as Record<string, unknown>).usage\n\t\t\t: undefined;\n\n\treturn baseCreateOpenRouter({\n\t\tapiKey: apiKey ?? process.env.OPENROUTER_API_KEY,\n\t\t...rest,\n\t\textraBody: {\n\t\t\t...extraBody,\n\t\t\tusage: {\n\t\t\t\tinclude: true,\n\t\t\t\t...(userUsage && typeof userUsage === \"object\"\n\t\t\t\t\t? (userUsage as Record<string, unknown>)\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t},\n\t});\n}\n\nexport type { OpenRouterProvider, OpenRouterProviderSettings };\n","import type { LanguageModelUsage, TextStreamPart, ToolSet } from \"ai\";\nimport chalk from \"chalk\";\n\ntype OpenRouterUsageCost = {\n\tcost?: number;\n\tcostDetails?: {\n\t\tupstreamInferenceCost?: number | null;\n\t};\n};\n\ntype OpenRouterModelResponse = {\n\tdata?: {\n\t\tcontext_length?: unknown;\n\t\ttop_provider?: {\n\t\t\tcontext_length?: unknown;\n\t\t};\n\t};\n};\n\nconst openRouterContextLengthCache = new Map<string, Promise<number | null>>();\n\nfunction getOpenRouterCost(part: TextStreamPart<ToolSet>) {\n\tif (part.type !== \"finish-step\") return null;\n\n\tconst metadataUsage = part.providerMetadata?.openrouter?.usage as\n\t\t| OpenRouterUsageCost\n\t\t| undefined;\n\tconst rawUsage = part.usage.raw as\n\t\t| {\n\t\t\t\tcost?: number;\n\t\t\t\tcost_details?: { upstream_inference_cost?: number | null };\n\t\t }\n\t\t| undefined;\n\n\tconst cost = metadataUsage?.cost ?? rawUsage?.cost;\n\tconst upstreamInferenceCost =\n\t\tmetadataUsage?.costDetails?.upstreamInferenceCost ??\n\t\trawUsage?.cost_details?.upstream_inference_cost;\n\n\t// OpenRouter credit usage reports `cost`; BYOK usage can report `cost: 0`\n\t// with the real provider charge in `upstreamInferenceCost`.\n\tif (cost === 0 && upstreamInferenceCost != null) {\n\t\treturn upstreamInferenceCost;\n\t}\n\n\treturn cost ?? upstreamInferenceCost ?? null;\n}\n\nfunction formatCost(cost: number) {\n\treturn `$${cost.toFixed(6)}`;\n}\n\nfunction formatTokens(tokens: number) {\n\treturn tokens.toLocaleString(\"en-US\");\n}\n\nfunction parsePositiveInteger(value: unknown) {\n\tif (typeof value === \"number\" && Number.isInteger(value) && value > 0) {\n\t\treturn value;\n\t}\n\n\tif (typeof value === \"string\") {\n\t\tconst parsed = Number(value);\n\t\tif (Number.isInteger(parsed) && parsed > 0) {\n\t\t\treturn parsed;\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction getOpenRouterContextLengthFromResponse(payload: unknown) {\n\tconst data = (payload as OpenRouterModelResponse).data;\n\treturn (\n\t\tparsePositiveInteger(data?.context_length) ??\n\t\tparsePositiveInteger(data?.top_provider?.context_length)\n\t);\n}\n\nasync function fetchOpenRouterContextLength(modelId: string) {\n\tconst pathModelId = modelId.split(\"/\").map(encodeURIComponent).join(\"/\");\n\tconst response = await fetch(\n\t\t`https://openrouter.ai/api/v1/model/${pathModelId}`,\n\t);\n\n\tif (!response.ok) {\n\t\treturn null;\n\t}\n\n\treturn getOpenRouterContextLengthFromResponse(await response.json());\n}\n\nfunction getOpenRouterContextLength(modelId: string) {\n\tconst cached = openRouterContextLengthCache.get(modelId);\n\tif (cached) return cached;\n\n\tconst promise = fetchOpenRouterContextLength(modelId).catch(() => null);\n\topenRouterContextLengthCache.set(modelId, promise);\n\treturn promise;\n}\n\nfunction getModelId(part: TextStreamPart<ToolSet>) {\n\tif (part.type === \"start-step\") {\n\t\tconst body = part.request.body;\n\t\tif (body && typeof body === \"object\" && \"model\" in body) {\n\t\t\tconst model = body.model;\n\t\t\tif (typeof model === \"string\" && model.length > 0) {\n\t\t\t\treturn model;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (part.type === \"finish-step\") {\n\t\treturn part.response.modelId;\n\t}\n\n\treturn null;\n}\n\nfunction getUsageTokens(usage: LanguageModelUsage | undefined) {\n\tif (!usage) return null;\n\n\t// Context usage is the stopping-point conversation size estimate:\n\t// the last step's input tokens are the full history before the final response,\n\t// and its output tokens are what will be appended to that history. An exact\n\t// \"next empty user message\" count would require another provider tokenization\n\t// pass because the stream does not emit usage for hypothetical future calls.\n\tif (usage.inputTokens != null || usage.outputTokens != null) {\n\t\treturn (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);\n\t}\n\n\treturn usage.totalTokens ?? null;\n}\n\nfunction formatContextUsage(\n\tusage: LanguageModelUsage | undefined,\n\tcontextWindowTokens: number | null,\n) {\n\tconst tokens = getUsageTokens(usage);\n\tif (tokens == null) return \"context used ?\";\n\n\tif (contextWindowTokens == null) {\n\t\treturn `context used ${formatTokens(tokens)} tokens`;\n\t}\n\n\tconst percentage = (tokens / contextWindowTokens) * 100;\n\tconst formattedPercentage =\n\t\tpercentage > 0 && percentage < 0.01 ? \"<0.01\" : percentage.toFixed(2);\n\n\treturn `${formattedPercentage}% context used (${formatTokens(tokens)} / ${formatTokens(contextWindowTokens)} tokens)`;\n}\n\n/**\n * Pretty-prints every event coming off an AI SDK full stream so you can watch\n * the model think, talk, and call tools in real time — and see live token /\n * cost accounting when the model is served through OpenRouter (see\n * {@link createOpenRouter}).\n *\n * Pass it the `fullStream` from `streamText`:\n *\n * @example\n * ```ts\n * import { streamText } from \"ai\";\n * import { createOpenRouter, logStream } from \"@merkie/agentic\";\n *\n * const openrouter = createOpenRouter();\n *\n * const result = streamText({\n * model: openrouter(\"openai/gpt-4o-mini\"),\n * prompt: \"Explain quantum tunneling in one paragraph.\",\n * });\n *\n * await logStream(result.fullStream);\n * ```\n */\nexport async function logStream(\n\tstream: AsyncIterable<TextStreamPart<ToolSet>>,\n): Promise<void> {\n\t// Track which \"channel\" (reasoning vs. text) we're currently writing to so we\n\t// only print a header when it changes, and stream deltas onto the same line.\n\tlet active: \"reasoning\" | \"text\" | null = null;\n\tlet openRouterCost = 0;\n\tlet hasOpenRouterCost = false;\n\tlet latestStepUsage: LanguageModelUsage | undefined;\n\tlet contextWindowPromise: Promise<number | null> | undefined;\n\n\tconst endActive = () => {\n\t\tif (active) process.stdout.write(\"\\n\");\n\t\tactive = null;\n\t};\n\n\tconst startContextWindowFetch = (modelId: string | null) => {\n\t\tif (!modelId || contextWindowPromise) return;\n\t\tcontextWindowPromise = getOpenRouterContextLength(modelId);\n\t};\n\n\tfor await (const part of stream) {\n\t\tswitch (part.type) {\n\t\t\tcase \"start-step\":\n\t\t\t\tendActive();\n\t\t\t\tstartContextWindowFetch(getModelId(part));\n\t\t\t\tconsole.log(chalk.dim(\"──────── step ────────\"));\n\t\t\t\tbreak;\n\n\t\t\tcase \"finish-step\": {\n\t\t\t\tlatestStepUsage = part.usage;\n\t\t\t\tstartContextWindowFetch(getModelId(part));\n\t\t\t\tconst cost = getOpenRouterCost(part);\n\t\t\t\tif (cost != null) {\n\t\t\t\t\topenRouterCost += cost;\n\t\t\t\t\thasOpenRouterCost = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"reasoning-delta\":\n\t\t\t\tif (active !== \"reasoning\") {\n\t\t\t\t\tendActive();\n\t\t\t\t\tprocess.stdout.write(chalk.magenta.bold(\"🧠 reasoning \"));\n\t\t\t\t\tactive = \"reasoning\";\n\t\t\t\t}\n\t\t\t\tprocess.stdout.write(chalk.magenta(part.text));\n\t\t\t\tbreak;\n\n\t\t\tcase \"text-delta\":\n\t\t\t\tif (active !== \"text\") {\n\t\t\t\t\tendActive();\n\t\t\t\t\tprocess.stdout.write(chalk.white.bold(\"💬 message \"));\n\t\t\t\t\tactive = \"text\";\n\t\t\t\t}\n\t\t\t\tprocess.stdout.write(chalk.white(part.text));\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool-call\":\n\t\t\t\tendActive();\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.cyan.bold(`🔧 tool call ${part.toolName}`) +\n\t\t\t\t\t\tchalk.cyan(`(${JSON.stringify(part.input)})`),\n\t\t\t\t);\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool-result\":\n\t\t\t\tendActive();\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.green.bold(`✅ tool result ${part.toolName} `) +\n\t\t\t\t\t\tchalk.green(JSON.stringify(part.output)),\n\t\t\t\t);\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool-error\":\n\t\t\t\tendActive();\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.red.bold(`❌ tool error ${part.toolName} `) +\n\t\t\t\t\t\tchalk.red(String(part.error)),\n\t\t\t\t);\n\t\t\t\tbreak;\n\n\t\t\tcase \"error\":\n\t\t\t\tendActive();\n\t\t\t\tconsole.log(chalk.red.bold(\"‼️ stream error \"), part.error);\n\t\t\t\tbreak;\n\n\t\t\tcase \"finish\": {\n\t\t\t\tendActive();\n\t\t\t\tconst contextWindowTokens = contextWindowPromise\n\t\t\t\t\t? await contextWindowPromise\n\t\t\t\t\t: null;\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.dim(\n\t\t\t\t\t\t`\\n── done (${part.finishReason}) · ${formatContextUsage(latestStepUsage, contextWindowTokens)}${hasOpenRouterCost ? ` · cost ${formatCost(openRouterCost)}` : \"\"} ──`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport default logStream;\n"],"mappings":"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","names":["baseCreateOpenRouter","chalk"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/openrouter.ts","../src/logStream.ts"],"sourcesContent":["export {\n\tcreateOpenRouter,\n\ttype OpenRouterProvider,\n\ttype OpenRouterProviderSettings,\n} from \"./openrouter.js\";\nexport { logStream } from \"./logStream.js\";\n","import {\n\tcreateOpenRouter as baseCreateOpenRouter,\n\ttype OpenRouterProvider,\n\ttype OpenRouterProviderSettings,\n} from \"@openrouter/ai-sdk-provider\";\n\n/**\n * Drop-in replacement for `createOpenRouter` from `@openrouter/ai-sdk-provider`.\n *\n * It behaves identically to the original — same options, same defaults\n * (including reading `OPENROUTER_API_KEY` from the environment) — with exactly\n * one thing added: `extraBody.usage.include` defaults to `true`, which tells\n * OpenRouter to return cost + token accounting on every response. That's what\n * powers the mid-run cost/usage tracking in {@link logStream}.\n *\n * Everything you pass through wins over that default — including `usage` — so it\n * stays 1:1 compatible with the upstream factory. Add your own `extraBody`, flip\n * `usage.include` off, etc.\n *\n * @example\n * ```ts\n * import { createOpenRouter } from \"@merkie/agentic\";\n *\n * // Same as the upstream factory, just with usage tracking switched on.\n * const openrouter = createOpenRouter();\n *\n * const model = openrouter(\"openai/gpt-4o-mini\");\n * ```\n */\nexport function createOpenRouter(\n\tsettings: OpenRouterProviderSettings = {},\n): OpenRouterProvider {\n\tconst { extraBody, ...rest } = settings;\n\n\tconst userUsage =\n\t\textraBody && typeof extraBody === \"object\" && \"usage\" in extraBody\n\t\t\t? (extraBody as Record<string, unknown>).usage\n\t\t\t: undefined;\n\n\treturn baseCreateOpenRouter({\n\t\t...rest,\n\t\textraBody: {\n\t\t\t...extraBody,\n\t\t\tusage: {\n\t\t\t\tinclude: true,\n\t\t\t\t...(userUsage && typeof userUsage === \"object\"\n\t\t\t\t\t? (userUsage as Record<string, unknown>)\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t},\n\t});\n}\n\nexport type { OpenRouterProvider, OpenRouterProviderSettings };\n","import type { LanguageModelUsage, TextStreamPart, ToolSet } from \"ai\";\nimport chalk from \"chalk\";\n\ntype OpenRouterUsageCost = {\n\tcost?: number;\n\tcostDetails?: {\n\t\tupstreamInferenceCost?: number | null;\n\t};\n};\n\ntype OpenRouterModelResponse = {\n\tdata?: {\n\t\tcontext_length?: unknown;\n\t\ttop_provider?: {\n\t\t\tcontext_length?: unknown;\n\t\t};\n\t};\n};\n\nconst openRouterContextLengthCache = new Map<string, Promise<number | null>>();\n\nfunction getOpenRouterCost(part: TextStreamPart<ToolSet>) {\n\tif (part.type !== \"finish-step\") return null;\n\n\tconst metadataUsage = part.providerMetadata?.openrouter?.usage as\n\t\t| OpenRouterUsageCost\n\t\t| undefined;\n\tconst rawUsage = part.usage.raw as\n\t\t| {\n\t\t\t\tcost?: number;\n\t\t\t\tcost_details?: { upstream_inference_cost?: number | null };\n\t\t }\n\t\t| undefined;\n\n\tconst cost = metadataUsage?.cost ?? rawUsage?.cost;\n\tconst upstreamInferenceCost =\n\t\tmetadataUsage?.costDetails?.upstreamInferenceCost ??\n\t\trawUsage?.cost_details?.upstream_inference_cost;\n\n\t// OpenRouter credit usage reports `cost`; BYOK usage can report `cost: 0`\n\t// with the real provider charge in `upstreamInferenceCost`.\n\tif (cost === 0 && upstreamInferenceCost != null) {\n\t\treturn upstreamInferenceCost;\n\t}\n\n\treturn cost ?? upstreamInferenceCost ?? null;\n}\n\nfunction formatCost(cost: number) {\n\treturn `$${cost.toFixed(6)}`;\n}\n\nfunction formatTokens(tokens: number) {\n\treturn tokens.toLocaleString(\"en-US\");\n}\n\nfunction parsePositiveInteger(value: unknown) {\n\tif (typeof value === \"number\" && Number.isInteger(value) && value > 0) {\n\t\treturn value;\n\t}\n\n\tif (typeof value === \"string\") {\n\t\tconst parsed = Number(value);\n\t\tif (Number.isInteger(parsed) && parsed > 0) {\n\t\t\treturn parsed;\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction getOpenRouterContextLengthFromResponse(payload: unknown) {\n\tconst data = (payload as OpenRouterModelResponse).data;\n\treturn (\n\t\tparsePositiveInteger(data?.context_length) ??\n\t\tparsePositiveInteger(data?.top_provider?.context_length)\n\t);\n}\n\nasync function fetchOpenRouterContextLength(modelId: string) {\n\tconst pathModelId = modelId.split(\"/\").map(encodeURIComponent).join(\"/\");\n\tconst response = await fetch(\n\t\t`https://openrouter.ai/api/v1/model/${pathModelId}`,\n\t);\n\n\tif (!response.ok) {\n\t\treturn null;\n\t}\n\n\treturn getOpenRouterContextLengthFromResponse(await response.json());\n}\n\nfunction getOpenRouterContextLength(modelId: string) {\n\tconst cached = openRouterContextLengthCache.get(modelId);\n\tif (cached) return cached;\n\n\tconst promise = fetchOpenRouterContextLength(modelId).catch(() => null);\n\topenRouterContextLengthCache.set(modelId, promise);\n\treturn promise;\n}\n\nfunction getModelId(part: TextStreamPart<ToolSet>) {\n\tif (part.type === \"start-step\") {\n\t\tconst body = part.request.body;\n\t\tif (body && typeof body === \"object\" && \"model\" in body) {\n\t\t\tconst model = body.model;\n\t\t\tif (typeof model === \"string\" && model.length > 0) {\n\t\t\t\treturn model;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (part.type === \"finish-step\") {\n\t\treturn part.response.modelId;\n\t}\n\n\treturn null;\n}\n\nfunction getUsageTokens(usage: LanguageModelUsage | undefined) {\n\tif (!usage) return null;\n\n\t// Context usage is the stopping-point conversation size estimate:\n\t// the last step's input tokens are the full history before the final response,\n\t// and its output tokens are what will be appended to that history. An exact\n\t// \"next empty user message\" count would require another provider tokenization\n\t// pass because the stream does not emit usage for hypothetical future calls.\n\tif (usage.inputTokens != null || usage.outputTokens != null) {\n\t\treturn (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);\n\t}\n\n\treturn usage.totalTokens ?? null;\n}\n\nfunction formatContextUsage(\n\tusage: LanguageModelUsage | undefined,\n\tcontextWindowTokens: number | null,\n) {\n\tconst tokens = getUsageTokens(usage);\n\tif (tokens == null) return \"context used ?\";\n\n\tif (contextWindowTokens == null) {\n\t\treturn `context used ${formatTokens(tokens)} tokens`;\n\t}\n\n\tconst percentage = (tokens / contextWindowTokens) * 100;\n\tconst formattedPercentage =\n\t\tpercentage > 0 && percentage < 0.01 ? \"<0.01\" : percentage.toFixed(2);\n\n\treturn `${formattedPercentage}% context used (${formatTokens(tokens)} / ${formatTokens(contextWindowTokens)} tokens)`;\n}\n\n/**\n * Pretty-prints every event coming off an AI SDK full stream so you can watch\n * the model think, talk, and call tools in real time — and see live token /\n * cost accounting when the model is served through OpenRouter (see\n * {@link createOpenRouter}).\n *\n * Pass it the `fullStream` from `streamText`:\n *\n * @example\n * ```ts\n * import { streamText } from \"ai\";\n * import { createOpenRouter, logStream } from \"@merkie/agentic\";\n *\n * const openrouter = createOpenRouter();\n *\n * const result = streamText({\n * model: openrouter(\"openai/gpt-4o-mini\"),\n * prompt: \"Explain quantum tunneling in one paragraph.\",\n * });\n *\n * await logStream(result.fullStream);\n * ```\n */\nexport async function logStream(\n\tstream: AsyncIterable<TextStreamPart<ToolSet>>,\n): Promise<void> {\n\t// Track which \"channel\" (reasoning vs. text) we're currently writing to so we\n\t// only print a header when it changes, and stream deltas onto the same line.\n\tlet active: \"reasoning\" | \"text\" | null = null;\n\tlet openRouterCost = 0;\n\tlet hasOpenRouterCost = false;\n\tlet latestStepUsage: LanguageModelUsage | undefined;\n\tlet contextWindowPromise: Promise<number | null> | undefined;\n\n\tconst endActive = () => {\n\t\tif (active) process.stdout.write(\"\\n\");\n\t\tactive = null;\n\t};\n\n\tconst startContextWindowFetch = (modelId: string | null) => {\n\t\tif (!modelId || contextWindowPromise) return;\n\t\tcontextWindowPromise = getOpenRouterContextLength(modelId);\n\t};\n\n\tfor await (const part of stream) {\n\t\tswitch (part.type) {\n\t\t\tcase \"start-step\":\n\t\t\t\tendActive();\n\t\t\t\tstartContextWindowFetch(getModelId(part));\n\t\t\t\tconsole.log(chalk.dim(\"──────── step ────────\"));\n\t\t\t\tbreak;\n\n\t\t\tcase \"finish-step\": {\n\t\t\t\tlatestStepUsage = part.usage;\n\t\t\t\tstartContextWindowFetch(getModelId(part));\n\t\t\t\tconst cost = getOpenRouterCost(part);\n\t\t\t\tif (cost != null) {\n\t\t\t\t\topenRouterCost += cost;\n\t\t\t\t\thasOpenRouterCost = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"reasoning-delta\":\n\t\t\t\tif (active !== \"reasoning\") {\n\t\t\t\t\tendActive();\n\t\t\t\t\tprocess.stdout.write(chalk.magenta.bold(\"🧠 reasoning \"));\n\t\t\t\t\tactive = \"reasoning\";\n\t\t\t\t}\n\t\t\t\tprocess.stdout.write(chalk.magenta(part.text));\n\t\t\t\tbreak;\n\n\t\t\tcase \"text-delta\":\n\t\t\t\tif (active !== \"text\") {\n\t\t\t\t\tendActive();\n\t\t\t\t\tprocess.stdout.write(chalk.white.bold(\"💬 message \"));\n\t\t\t\t\tactive = \"text\";\n\t\t\t\t}\n\t\t\t\tprocess.stdout.write(chalk.white(part.text));\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool-call\":\n\t\t\t\tendActive();\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.cyan.bold(`🔧 tool call ${part.toolName}`) +\n\t\t\t\t\t\tchalk.cyan(`(${JSON.stringify(part.input)})`),\n\t\t\t\t);\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool-result\":\n\t\t\t\tendActive();\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.green.bold(`✅ tool result ${part.toolName} `) +\n\t\t\t\t\t\tchalk.green(JSON.stringify(part.output)),\n\t\t\t\t);\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool-error\":\n\t\t\t\tendActive();\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.red.bold(`❌ tool error ${part.toolName} `) +\n\t\t\t\t\t\tchalk.red(String(part.error)),\n\t\t\t\t);\n\t\t\t\tbreak;\n\n\t\t\tcase \"error\":\n\t\t\t\tendActive();\n\t\t\t\tconsole.log(chalk.red.bold(\"‼️ stream error \"), part.error);\n\t\t\t\tbreak;\n\n\t\t\tcase \"finish\": {\n\t\t\t\tendActive();\n\t\t\t\tconst contextWindowTokens = contextWindowPromise\n\t\t\t\t\t? await contextWindowPromise\n\t\t\t\t\t: null;\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.dim(\n\t\t\t\t\t\t`\\n── done (${part.finishReason}) · ${formatContextUsage(latestStepUsage, contextWindowTokens)}${hasOpenRouterCost ? ` · cost ${formatCost(openRouterCost)}` : \"\"} ──`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport default logStream;\n"],"mappings":"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","names":["baseCreateOpenRouter","chalk"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -5,23 +5,21 @@ import { TextStreamPart, ToolSet } from 'ai';
|
|
|
5
5
|
/**
|
|
6
6
|
* Drop-in replacement for `createOpenRouter` from `@openrouter/ai-sdk-provider`.
|
|
7
7
|
*
|
|
8
|
-
* It behaves identically to the original
|
|
8
|
+
* It behaves identically to the original — same options, same defaults
|
|
9
|
+
* (including reading `OPENROUTER_API_KEY` from the environment) — with exactly
|
|
10
|
+
* one thing added: `extraBody.usage.include` defaults to `true`, which tells
|
|
11
|
+
* OpenRouter to return cost + token accounting on every response. That's what
|
|
12
|
+
* powers the mid-run cost/usage tracking in {@link logStream}.
|
|
9
13
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* return cost + token accounting on every response. This is what powers the
|
|
14
|
-
* mid-run cost/usage tracking in {@link logStream}.
|
|
15
|
-
*
|
|
16
|
-
* Every option you pass through wins over the defaults — including `usage` — so
|
|
17
|
-
* this is 1:1 compatible with the upstream `createOpenRouter`. You can override
|
|
18
|
-
* the api key, add your own `extraBody`, flip `usage.include` off, etc.
|
|
14
|
+
* Everything you pass through wins over that default — including `usage` — so it
|
|
15
|
+
* stays 1:1 compatible with the upstream factory. Add your own `extraBody`, flip
|
|
16
|
+
* `usage.include` off, etc.
|
|
19
17
|
*
|
|
20
18
|
* @example
|
|
21
19
|
* ```ts
|
|
22
20
|
* import { createOpenRouter } from "@merkie/agentic";
|
|
23
21
|
*
|
|
24
|
-
* //
|
|
22
|
+
* // Same as the upstream factory, just with usage tracking switched on.
|
|
25
23
|
* const openrouter = createOpenRouter();
|
|
26
24
|
*
|
|
27
25
|
* const model = openrouter("openai/gpt-4o-mini");
|
package/dist/index.d.ts
CHANGED
|
@@ -5,23 +5,21 @@ import { TextStreamPart, ToolSet } from 'ai';
|
|
|
5
5
|
/**
|
|
6
6
|
* Drop-in replacement for `createOpenRouter` from `@openrouter/ai-sdk-provider`.
|
|
7
7
|
*
|
|
8
|
-
* It behaves identically to the original
|
|
8
|
+
* It behaves identically to the original — same options, same defaults
|
|
9
|
+
* (including reading `OPENROUTER_API_KEY` from the environment) — with exactly
|
|
10
|
+
* one thing added: `extraBody.usage.include` defaults to `true`, which tells
|
|
11
|
+
* OpenRouter to return cost + token accounting on every response. That's what
|
|
12
|
+
* powers the mid-run cost/usage tracking in {@link logStream}.
|
|
9
13
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* return cost + token accounting on every response. This is what powers the
|
|
14
|
-
* mid-run cost/usage tracking in {@link logStream}.
|
|
15
|
-
*
|
|
16
|
-
* Every option you pass through wins over the defaults — including `usage` — so
|
|
17
|
-
* this is 1:1 compatible with the upstream `createOpenRouter`. You can override
|
|
18
|
-
* the api key, add your own `extraBody`, flip `usage.include` off, etc.
|
|
14
|
+
* Everything you pass through wins over that default — including `usage` — so it
|
|
15
|
+
* stays 1:1 compatible with the upstream factory. Add your own `extraBody`, flip
|
|
16
|
+
* `usage.include` off, etc.
|
|
19
17
|
*
|
|
20
18
|
* @example
|
|
21
19
|
* ```ts
|
|
22
20
|
* import { createOpenRouter } from "@merkie/agentic";
|
|
23
21
|
*
|
|
24
|
-
* //
|
|
22
|
+
* // Same as the upstream factory, just with usage tracking switched on.
|
|
25
23
|
* const openrouter = createOpenRouter();
|
|
26
24
|
*
|
|
27
25
|
* const model = openrouter("openai/gpt-4o-mini");
|
package/dist/index.js
CHANGED
|
@@ -3,10 +3,9 @@ import {
|
|
|
3
3
|
createOpenRouter as baseCreateOpenRouter
|
|
4
4
|
} from "@openrouter/ai-sdk-provider";
|
|
5
5
|
function createOpenRouter(settings = {}) {
|
|
6
|
-
const {
|
|
6
|
+
const { extraBody, ...rest } = settings;
|
|
7
7
|
const userUsage = extraBody && typeof extraBody === "object" && "usage" in extraBody ? extraBody.usage : void 0;
|
|
8
8
|
return baseCreateOpenRouter({
|
|
9
|
-
apiKey: apiKey ?? process.env.OPENROUTER_API_KEY,
|
|
10
9
|
...rest,
|
|
11
10
|
extraBody: {
|
|
12
11
|
...extraBody,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/openrouter.ts","../src/logStream.ts"],"sourcesContent":["import {\n\tcreateOpenRouter as baseCreateOpenRouter,\n\ttype OpenRouterProvider,\n\ttype OpenRouterProviderSettings,\n} from \"@openrouter/ai-sdk-provider\";\n\n/**\n * Drop-in replacement for `createOpenRouter` from `@openrouter/ai-sdk-provider`.\n *\n * It behaves identically to the original, with two conveniences baked in:\n *\n * 1. `apiKey` defaults to `process.env.OPENROUTER_API_KEY` when you don't pass\n * one, so the common case is just `createOpenRouter()`.\n * 2. `extraBody.usage.include` defaults to `true`, which tells OpenRouter to\n * return cost + token accounting on every response. This is what powers the\n * mid-run cost/usage tracking in {@link logStream}.\n *\n * Every option you pass through wins over the defaults — including `usage` — so\n * this is 1:1 compatible with the upstream `createOpenRouter`. You can override\n * the api key, add your own `extraBody`, flip `usage.include` off, etc.\n *\n * @example\n * ```ts\n * import { createOpenRouter } from \"@merkie/agentic\";\n *\n * // Reads OPENROUTER_API_KEY from the environment, usage tracking on.\n * const openrouter = createOpenRouter();\n *\n * const model = openrouter(\"openai/gpt-4o-mini\");\n * ```\n */\nexport function createOpenRouter(\n\tsettings: OpenRouterProviderSettings = {},\n): OpenRouterProvider {\n\tconst { apiKey, extraBody, ...rest } = settings;\n\n\tconst userUsage =\n\t\textraBody && typeof extraBody === \"object\" && \"usage\" in extraBody\n\t\t\t? (extraBody as Record<string, unknown>).usage\n\t\t\t: undefined;\n\n\treturn baseCreateOpenRouter({\n\t\tapiKey: apiKey ?? process.env.OPENROUTER_API_KEY,\n\t\t...rest,\n\t\textraBody: {\n\t\t\t...extraBody,\n\t\t\tusage: {\n\t\t\t\tinclude: true,\n\t\t\t\t...(userUsage && typeof userUsage === \"object\"\n\t\t\t\t\t? (userUsage as Record<string, unknown>)\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t},\n\t});\n}\n\nexport type { OpenRouterProvider, OpenRouterProviderSettings };\n","import type { LanguageModelUsage, TextStreamPart, ToolSet } from \"ai\";\nimport chalk from \"chalk\";\n\ntype OpenRouterUsageCost = {\n\tcost?: number;\n\tcostDetails?: {\n\t\tupstreamInferenceCost?: number | null;\n\t};\n};\n\ntype OpenRouterModelResponse = {\n\tdata?: {\n\t\tcontext_length?: unknown;\n\t\ttop_provider?: {\n\t\t\tcontext_length?: unknown;\n\t\t};\n\t};\n};\n\nconst openRouterContextLengthCache = new Map<string, Promise<number | null>>();\n\nfunction getOpenRouterCost(part: TextStreamPart<ToolSet>) {\n\tif (part.type !== \"finish-step\") return null;\n\n\tconst metadataUsage = part.providerMetadata?.openrouter?.usage as\n\t\t| OpenRouterUsageCost\n\t\t| undefined;\n\tconst rawUsage = part.usage.raw as\n\t\t| {\n\t\t\t\tcost?: number;\n\t\t\t\tcost_details?: { upstream_inference_cost?: number | null };\n\t\t }\n\t\t| undefined;\n\n\tconst cost = metadataUsage?.cost ?? rawUsage?.cost;\n\tconst upstreamInferenceCost =\n\t\tmetadataUsage?.costDetails?.upstreamInferenceCost ??\n\t\trawUsage?.cost_details?.upstream_inference_cost;\n\n\t// OpenRouter credit usage reports `cost`; BYOK usage can report `cost: 0`\n\t// with the real provider charge in `upstreamInferenceCost`.\n\tif (cost === 0 && upstreamInferenceCost != null) {\n\t\treturn upstreamInferenceCost;\n\t}\n\n\treturn cost ?? upstreamInferenceCost ?? null;\n}\n\nfunction formatCost(cost: number) {\n\treturn `$${cost.toFixed(6)}`;\n}\n\nfunction formatTokens(tokens: number) {\n\treturn tokens.toLocaleString(\"en-US\");\n}\n\nfunction parsePositiveInteger(value: unknown) {\n\tif (typeof value === \"number\" && Number.isInteger(value) && value > 0) {\n\t\treturn value;\n\t}\n\n\tif (typeof value === \"string\") {\n\t\tconst parsed = Number(value);\n\t\tif (Number.isInteger(parsed) && parsed > 0) {\n\t\t\treturn parsed;\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction getOpenRouterContextLengthFromResponse(payload: unknown) {\n\tconst data = (payload as OpenRouterModelResponse).data;\n\treturn (\n\t\tparsePositiveInteger(data?.context_length) ??\n\t\tparsePositiveInteger(data?.top_provider?.context_length)\n\t);\n}\n\nasync function fetchOpenRouterContextLength(modelId: string) {\n\tconst pathModelId = modelId.split(\"/\").map(encodeURIComponent).join(\"/\");\n\tconst response = await fetch(\n\t\t`https://openrouter.ai/api/v1/model/${pathModelId}`,\n\t);\n\n\tif (!response.ok) {\n\t\treturn null;\n\t}\n\n\treturn getOpenRouterContextLengthFromResponse(await response.json());\n}\n\nfunction getOpenRouterContextLength(modelId: string) {\n\tconst cached = openRouterContextLengthCache.get(modelId);\n\tif (cached) return cached;\n\n\tconst promise = fetchOpenRouterContextLength(modelId).catch(() => null);\n\topenRouterContextLengthCache.set(modelId, promise);\n\treturn promise;\n}\n\nfunction getModelId(part: TextStreamPart<ToolSet>) {\n\tif (part.type === \"start-step\") {\n\t\tconst body = part.request.body;\n\t\tif (body && typeof body === \"object\" && \"model\" in body) {\n\t\t\tconst model = body.model;\n\t\t\tif (typeof model === \"string\" && model.length > 0) {\n\t\t\t\treturn model;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (part.type === \"finish-step\") {\n\t\treturn part.response.modelId;\n\t}\n\n\treturn null;\n}\n\nfunction getUsageTokens(usage: LanguageModelUsage | undefined) {\n\tif (!usage) return null;\n\n\t// Context usage is the stopping-point conversation size estimate:\n\t// the last step's input tokens are the full history before the final response,\n\t// and its output tokens are what will be appended to that history. An exact\n\t// \"next empty user message\" count would require another provider tokenization\n\t// pass because the stream does not emit usage for hypothetical future calls.\n\tif (usage.inputTokens != null || usage.outputTokens != null) {\n\t\treturn (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);\n\t}\n\n\treturn usage.totalTokens ?? null;\n}\n\nfunction formatContextUsage(\n\tusage: LanguageModelUsage | undefined,\n\tcontextWindowTokens: number | null,\n) {\n\tconst tokens = getUsageTokens(usage);\n\tif (tokens == null) return \"context used ?\";\n\n\tif (contextWindowTokens == null) {\n\t\treturn `context used ${formatTokens(tokens)} tokens`;\n\t}\n\n\tconst percentage = (tokens / contextWindowTokens) * 100;\n\tconst formattedPercentage =\n\t\tpercentage > 0 && percentage < 0.01 ? \"<0.01\" : percentage.toFixed(2);\n\n\treturn `${formattedPercentage}% context used (${formatTokens(tokens)} / ${formatTokens(contextWindowTokens)} tokens)`;\n}\n\n/**\n * Pretty-prints every event coming off an AI SDK full stream so you can watch\n * the model think, talk, and call tools in real time — and see live token /\n * cost accounting when the model is served through OpenRouter (see\n * {@link createOpenRouter}).\n *\n * Pass it the `fullStream` from `streamText`:\n *\n * @example\n * ```ts\n * import { streamText } from \"ai\";\n * import { createOpenRouter, logStream } from \"@merkie/agentic\";\n *\n * const openrouter = createOpenRouter();\n *\n * const result = streamText({\n * model: openrouter(\"openai/gpt-4o-mini\"),\n * prompt: \"Explain quantum tunneling in one paragraph.\",\n * });\n *\n * await logStream(result.fullStream);\n * ```\n */\nexport async function logStream(\n\tstream: AsyncIterable<TextStreamPart<ToolSet>>,\n): Promise<void> {\n\t// Track which \"channel\" (reasoning vs. text) we're currently writing to so we\n\t// only print a header when it changes, and stream deltas onto the same line.\n\tlet active: \"reasoning\" | \"text\" | null = null;\n\tlet openRouterCost = 0;\n\tlet hasOpenRouterCost = false;\n\tlet latestStepUsage: LanguageModelUsage | undefined;\n\tlet contextWindowPromise: Promise<number | null> | undefined;\n\n\tconst endActive = () => {\n\t\tif (active) process.stdout.write(\"\\n\");\n\t\tactive = null;\n\t};\n\n\tconst startContextWindowFetch = (modelId: string | null) => {\n\t\tif (!modelId || contextWindowPromise) return;\n\t\tcontextWindowPromise = getOpenRouterContextLength(modelId);\n\t};\n\n\tfor await (const part of stream) {\n\t\tswitch (part.type) {\n\t\t\tcase \"start-step\":\n\t\t\t\tendActive();\n\t\t\t\tstartContextWindowFetch(getModelId(part));\n\t\t\t\tconsole.log(chalk.dim(\"──────── step ────────\"));\n\t\t\t\tbreak;\n\n\t\t\tcase \"finish-step\": {\n\t\t\t\tlatestStepUsage = part.usage;\n\t\t\t\tstartContextWindowFetch(getModelId(part));\n\t\t\t\tconst cost = getOpenRouterCost(part);\n\t\t\t\tif (cost != null) {\n\t\t\t\t\topenRouterCost += cost;\n\t\t\t\t\thasOpenRouterCost = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"reasoning-delta\":\n\t\t\t\tif (active !== \"reasoning\") {\n\t\t\t\t\tendActive();\n\t\t\t\t\tprocess.stdout.write(chalk.magenta.bold(\"🧠 reasoning \"));\n\t\t\t\t\tactive = \"reasoning\";\n\t\t\t\t}\n\t\t\t\tprocess.stdout.write(chalk.magenta(part.text));\n\t\t\t\tbreak;\n\n\t\t\tcase \"text-delta\":\n\t\t\t\tif (active !== \"text\") {\n\t\t\t\t\tendActive();\n\t\t\t\t\tprocess.stdout.write(chalk.white.bold(\"💬 message \"));\n\t\t\t\t\tactive = \"text\";\n\t\t\t\t}\n\t\t\t\tprocess.stdout.write(chalk.white(part.text));\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool-call\":\n\t\t\t\tendActive();\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.cyan.bold(`🔧 tool call ${part.toolName}`) +\n\t\t\t\t\t\tchalk.cyan(`(${JSON.stringify(part.input)})`),\n\t\t\t\t);\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool-result\":\n\t\t\t\tendActive();\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.green.bold(`✅ tool result ${part.toolName} `) +\n\t\t\t\t\t\tchalk.green(JSON.stringify(part.output)),\n\t\t\t\t);\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool-error\":\n\t\t\t\tendActive();\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.red.bold(`❌ tool error ${part.toolName} `) +\n\t\t\t\t\t\tchalk.red(String(part.error)),\n\t\t\t\t);\n\t\t\t\tbreak;\n\n\t\t\tcase \"error\":\n\t\t\t\tendActive();\n\t\t\t\tconsole.log(chalk.red.bold(\"‼️ stream error \"), part.error);\n\t\t\t\tbreak;\n\n\t\t\tcase \"finish\": {\n\t\t\t\tendActive();\n\t\t\t\tconst contextWindowTokens = contextWindowPromise\n\t\t\t\t\t? await contextWindowPromise\n\t\t\t\t\t: null;\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.dim(\n\t\t\t\t\t\t`\\n── done (${part.finishReason}) · ${formatContextUsage(latestStepUsage, contextWindowTokens)}${hasOpenRouterCost ? ` · cost ${formatCost(openRouterCost)}` : \"\"} ──`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport default logStream;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/openrouter.ts","../src/logStream.ts"],"sourcesContent":["import {\n\tcreateOpenRouter as baseCreateOpenRouter,\n\ttype OpenRouterProvider,\n\ttype OpenRouterProviderSettings,\n} from \"@openrouter/ai-sdk-provider\";\n\n/**\n * Drop-in replacement for `createOpenRouter` from `@openrouter/ai-sdk-provider`.\n *\n * It behaves identically to the original — same options, same defaults\n * (including reading `OPENROUTER_API_KEY` from the environment) — with exactly\n * one thing added: `extraBody.usage.include` defaults to `true`, which tells\n * OpenRouter to return cost + token accounting on every response. That's what\n * powers the mid-run cost/usage tracking in {@link logStream}.\n *\n * Everything you pass through wins over that default — including `usage` — so it\n * stays 1:1 compatible with the upstream factory. Add your own `extraBody`, flip\n * `usage.include` off, etc.\n *\n * @example\n * ```ts\n * import { createOpenRouter } from \"@merkie/agentic\";\n *\n * // Same as the upstream factory, just with usage tracking switched on.\n * const openrouter = createOpenRouter();\n *\n * const model = openrouter(\"openai/gpt-4o-mini\");\n * ```\n */\nexport function createOpenRouter(\n\tsettings: OpenRouterProviderSettings = {},\n): OpenRouterProvider {\n\tconst { extraBody, ...rest } = settings;\n\n\tconst userUsage =\n\t\textraBody && typeof extraBody === \"object\" && \"usage\" in extraBody\n\t\t\t? (extraBody as Record<string, unknown>).usage\n\t\t\t: undefined;\n\n\treturn baseCreateOpenRouter({\n\t\t...rest,\n\t\textraBody: {\n\t\t\t...extraBody,\n\t\t\tusage: {\n\t\t\t\tinclude: true,\n\t\t\t\t...(userUsage && typeof userUsage === \"object\"\n\t\t\t\t\t? (userUsage as Record<string, unknown>)\n\t\t\t\t\t: {}),\n\t\t\t},\n\t\t},\n\t});\n}\n\nexport type { OpenRouterProvider, OpenRouterProviderSettings };\n","import type { LanguageModelUsage, TextStreamPart, ToolSet } from \"ai\";\nimport chalk from \"chalk\";\n\ntype OpenRouterUsageCost = {\n\tcost?: number;\n\tcostDetails?: {\n\t\tupstreamInferenceCost?: number | null;\n\t};\n};\n\ntype OpenRouterModelResponse = {\n\tdata?: {\n\t\tcontext_length?: unknown;\n\t\ttop_provider?: {\n\t\t\tcontext_length?: unknown;\n\t\t};\n\t};\n};\n\nconst openRouterContextLengthCache = new Map<string, Promise<number | null>>();\n\nfunction getOpenRouterCost(part: TextStreamPart<ToolSet>) {\n\tif (part.type !== \"finish-step\") return null;\n\n\tconst metadataUsage = part.providerMetadata?.openrouter?.usage as\n\t\t| OpenRouterUsageCost\n\t\t| undefined;\n\tconst rawUsage = part.usage.raw as\n\t\t| {\n\t\t\t\tcost?: number;\n\t\t\t\tcost_details?: { upstream_inference_cost?: number | null };\n\t\t }\n\t\t| undefined;\n\n\tconst cost = metadataUsage?.cost ?? rawUsage?.cost;\n\tconst upstreamInferenceCost =\n\t\tmetadataUsage?.costDetails?.upstreamInferenceCost ??\n\t\trawUsage?.cost_details?.upstream_inference_cost;\n\n\t// OpenRouter credit usage reports `cost`; BYOK usage can report `cost: 0`\n\t// with the real provider charge in `upstreamInferenceCost`.\n\tif (cost === 0 && upstreamInferenceCost != null) {\n\t\treturn upstreamInferenceCost;\n\t}\n\n\treturn cost ?? upstreamInferenceCost ?? null;\n}\n\nfunction formatCost(cost: number) {\n\treturn `$${cost.toFixed(6)}`;\n}\n\nfunction formatTokens(tokens: number) {\n\treturn tokens.toLocaleString(\"en-US\");\n}\n\nfunction parsePositiveInteger(value: unknown) {\n\tif (typeof value === \"number\" && Number.isInteger(value) && value > 0) {\n\t\treturn value;\n\t}\n\n\tif (typeof value === \"string\") {\n\t\tconst parsed = Number(value);\n\t\tif (Number.isInteger(parsed) && parsed > 0) {\n\t\t\treturn parsed;\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction getOpenRouterContextLengthFromResponse(payload: unknown) {\n\tconst data = (payload as OpenRouterModelResponse).data;\n\treturn (\n\t\tparsePositiveInteger(data?.context_length) ??\n\t\tparsePositiveInteger(data?.top_provider?.context_length)\n\t);\n}\n\nasync function fetchOpenRouterContextLength(modelId: string) {\n\tconst pathModelId = modelId.split(\"/\").map(encodeURIComponent).join(\"/\");\n\tconst response = await fetch(\n\t\t`https://openrouter.ai/api/v1/model/${pathModelId}`,\n\t);\n\n\tif (!response.ok) {\n\t\treturn null;\n\t}\n\n\treturn getOpenRouterContextLengthFromResponse(await response.json());\n}\n\nfunction getOpenRouterContextLength(modelId: string) {\n\tconst cached = openRouterContextLengthCache.get(modelId);\n\tif (cached) return cached;\n\n\tconst promise = fetchOpenRouterContextLength(modelId).catch(() => null);\n\topenRouterContextLengthCache.set(modelId, promise);\n\treturn promise;\n}\n\nfunction getModelId(part: TextStreamPart<ToolSet>) {\n\tif (part.type === \"start-step\") {\n\t\tconst body = part.request.body;\n\t\tif (body && typeof body === \"object\" && \"model\" in body) {\n\t\t\tconst model = body.model;\n\t\t\tif (typeof model === \"string\" && model.length > 0) {\n\t\t\t\treturn model;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (part.type === \"finish-step\") {\n\t\treturn part.response.modelId;\n\t}\n\n\treturn null;\n}\n\nfunction getUsageTokens(usage: LanguageModelUsage | undefined) {\n\tif (!usage) return null;\n\n\t// Context usage is the stopping-point conversation size estimate:\n\t// the last step's input tokens are the full history before the final response,\n\t// and its output tokens are what will be appended to that history. An exact\n\t// \"next empty user message\" count would require another provider tokenization\n\t// pass because the stream does not emit usage for hypothetical future calls.\n\tif (usage.inputTokens != null || usage.outputTokens != null) {\n\t\treturn (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);\n\t}\n\n\treturn usage.totalTokens ?? null;\n}\n\nfunction formatContextUsage(\n\tusage: LanguageModelUsage | undefined,\n\tcontextWindowTokens: number | null,\n) {\n\tconst tokens = getUsageTokens(usage);\n\tif (tokens == null) return \"context used ?\";\n\n\tif (contextWindowTokens == null) {\n\t\treturn `context used ${formatTokens(tokens)} tokens`;\n\t}\n\n\tconst percentage = (tokens / contextWindowTokens) * 100;\n\tconst formattedPercentage =\n\t\tpercentage > 0 && percentage < 0.01 ? \"<0.01\" : percentage.toFixed(2);\n\n\treturn `${formattedPercentage}% context used (${formatTokens(tokens)} / ${formatTokens(contextWindowTokens)} tokens)`;\n}\n\n/**\n * Pretty-prints every event coming off an AI SDK full stream so you can watch\n * the model think, talk, and call tools in real time — and see live token /\n * cost accounting when the model is served through OpenRouter (see\n * {@link createOpenRouter}).\n *\n * Pass it the `fullStream` from `streamText`:\n *\n * @example\n * ```ts\n * import { streamText } from \"ai\";\n * import { createOpenRouter, logStream } from \"@merkie/agentic\";\n *\n * const openrouter = createOpenRouter();\n *\n * const result = streamText({\n * model: openrouter(\"openai/gpt-4o-mini\"),\n * prompt: \"Explain quantum tunneling in one paragraph.\",\n * });\n *\n * await logStream(result.fullStream);\n * ```\n */\nexport async function logStream(\n\tstream: AsyncIterable<TextStreamPart<ToolSet>>,\n): Promise<void> {\n\t// Track which \"channel\" (reasoning vs. text) we're currently writing to so we\n\t// only print a header when it changes, and stream deltas onto the same line.\n\tlet active: \"reasoning\" | \"text\" | null = null;\n\tlet openRouterCost = 0;\n\tlet hasOpenRouterCost = false;\n\tlet latestStepUsage: LanguageModelUsage | undefined;\n\tlet contextWindowPromise: Promise<number | null> | undefined;\n\n\tconst endActive = () => {\n\t\tif (active) process.stdout.write(\"\\n\");\n\t\tactive = null;\n\t};\n\n\tconst startContextWindowFetch = (modelId: string | null) => {\n\t\tif (!modelId || contextWindowPromise) return;\n\t\tcontextWindowPromise = getOpenRouterContextLength(modelId);\n\t};\n\n\tfor await (const part of stream) {\n\t\tswitch (part.type) {\n\t\t\tcase \"start-step\":\n\t\t\t\tendActive();\n\t\t\t\tstartContextWindowFetch(getModelId(part));\n\t\t\t\tconsole.log(chalk.dim(\"──────── step ────────\"));\n\t\t\t\tbreak;\n\n\t\t\tcase \"finish-step\": {\n\t\t\t\tlatestStepUsage = part.usage;\n\t\t\t\tstartContextWindowFetch(getModelId(part));\n\t\t\t\tconst cost = getOpenRouterCost(part);\n\t\t\t\tif (cost != null) {\n\t\t\t\t\topenRouterCost += cost;\n\t\t\t\t\thasOpenRouterCost = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"reasoning-delta\":\n\t\t\t\tif (active !== \"reasoning\") {\n\t\t\t\t\tendActive();\n\t\t\t\t\tprocess.stdout.write(chalk.magenta.bold(\"🧠 reasoning \"));\n\t\t\t\t\tactive = \"reasoning\";\n\t\t\t\t}\n\t\t\t\tprocess.stdout.write(chalk.magenta(part.text));\n\t\t\t\tbreak;\n\n\t\t\tcase \"text-delta\":\n\t\t\t\tif (active !== \"text\") {\n\t\t\t\t\tendActive();\n\t\t\t\t\tprocess.stdout.write(chalk.white.bold(\"💬 message \"));\n\t\t\t\t\tactive = \"text\";\n\t\t\t\t}\n\t\t\t\tprocess.stdout.write(chalk.white(part.text));\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool-call\":\n\t\t\t\tendActive();\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.cyan.bold(`🔧 tool call ${part.toolName}`) +\n\t\t\t\t\t\tchalk.cyan(`(${JSON.stringify(part.input)})`),\n\t\t\t\t);\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool-result\":\n\t\t\t\tendActive();\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.green.bold(`✅ tool result ${part.toolName} `) +\n\t\t\t\t\t\tchalk.green(JSON.stringify(part.output)),\n\t\t\t\t);\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool-error\":\n\t\t\t\tendActive();\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.red.bold(`❌ tool error ${part.toolName} `) +\n\t\t\t\t\t\tchalk.red(String(part.error)),\n\t\t\t\t);\n\t\t\t\tbreak;\n\n\t\t\tcase \"error\":\n\t\t\t\tendActive();\n\t\t\t\tconsole.log(chalk.red.bold(\"‼️ stream error \"), part.error);\n\t\t\t\tbreak;\n\n\t\t\tcase \"finish\": {\n\t\t\t\tendActive();\n\t\t\t\tconst contextWindowTokens = contextWindowPromise\n\t\t\t\t\t? await contextWindowPromise\n\t\t\t\t\t: null;\n\t\t\t\tconsole.log(\n\t\t\t\t\tchalk.dim(\n\t\t\t\t\t\t`\\n── done (${part.finishReason}) · ${formatContextUsage(latestStepUsage, contextWindowTokens)}${hasOpenRouterCost ? ` · cost ${formatCost(openRouterCost)}` : \"\"} ──`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport default logStream;\n"],"mappings":";AAAA;AAAA,EACC,oBAAoB;AAAA,OAGd;AAyBA,SAAS,iBACf,WAAuC,CAAC,GACnB;AACrB,QAAM,EAAE,WAAW,GAAG,KAAK,IAAI;AAE/B,QAAM,YACL,aAAa,OAAO,cAAc,YAAY,WAAW,YACrD,UAAsC,QACvC;AAEJ,SAAO,qBAAqB;AAAA,IAC3B,GAAG;AAAA,IACH,WAAW;AAAA,MACV,GAAG;AAAA,MACH,OAAO;AAAA,QACN,SAAS;AAAA,QACT,GAAI,aAAa,OAAO,cAAc,WAClC,YACD,CAAC;AAAA,MACL;AAAA,IACD;AAAA,EACD,CAAC;AACF;;;AClDA,OAAO,WAAW;AAkBlB,IAAM,+BAA+B,oBAAI,IAAoC;AAE7E,SAAS,kBAAkB,MAA+B;AACzD,MAAI,KAAK,SAAS,cAAe,QAAO;AAExC,QAAM,gBAAgB,KAAK,kBAAkB,YAAY;AAGzD,QAAM,WAAW,KAAK,MAAM;AAO5B,QAAM,OAAO,eAAe,QAAQ,UAAU;AAC9C,QAAM,wBACL,eAAe,aAAa,yBAC5B,UAAU,cAAc;AAIzB,MAAI,SAAS,KAAK,yBAAyB,MAAM;AAChD,WAAO;AAAA,EACR;AAEA,SAAO,QAAQ,yBAAyB;AACzC;AAEA,SAAS,WAAW,MAAc;AACjC,SAAO,IAAI,KAAK,QAAQ,CAAC,CAAC;AAC3B;AAEA,SAAS,aAAa,QAAgB;AACrC,SAAO,OAAO,eAAe,OAAO;AACrC;AAEA,SAAS,qBAAqB,OAAgB;AAC7C,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,KAAK,KAAK,QAAQ,GAAG;AACtE,WAAO;AAAA,EACR;AAEA,MAAI,OAAO,UAAU,UAAU;AAC9B,UAAM,SAAS,OAAO,KAAK;AAC3B,QAAI,OAAO,UAAU,MAAM,KAAK,SAAS,GAAG;AAC3C,aAAO;AAAA,IACR;AAAA,EACD;AAEA,SAAO;AACR;AAEA,SAAS,uCAAuC,SAAkB;AACjE,QAAM,OAAQ,QAAoC;AAClD,SACC,qBAAqB,MAAM,cAAc,KACzC,qBAAqB,MAAM,cAAc,cAAc;AAEzD;AAEA,eAAe,6BAA6B,SAAiB;AAC5D,QAAM,cAAc,QAAQ,MAAM,GAAG,EAAE,IAAI,kBAAkB,EAAE,KAAK,GAAG;AACvE,QAAM,WAAW,MAAM;AAAA,IACtB,sCAAsC,WAAW;AAAA,EAClD;AAEA,MAAI,CAAC,SAAS,IAAI;AACjB,WAAO;AAAA,EACR;AAEA,SAAO,uCAAuC,MAAM,SAAS,KAAK,CAAC;AACpE;AAEA,SAAS,2BAA2B,SAAiB;AACpD,QAAM,SAAS,6BAA6B,IAAI,OAAO;AACvD,MAAI,OAAQ,QAAO;AAEnB,QAAM,UAAU,6BAA6B,OAAO,EAAE,MAAM,MAAM,IAAI;AACtE,+BAA6B,IAAI,SAAS,OAAO;AACjD,SAAO;AACR;AAEA,SAAS,WAAW,MAA+B;AAClD,MAAI,KAAK,SAAS,cAAc;AAC/B,UAAM,OAAO,KAAK,QAAQ;AAC1B,QAAI,QAAQ,OAAO,SAAS,YAAY,WAAW,MAAM;AACxD,YAAM,QAAQ,KAAK;AACnB,UAAI,OAAO,UAAU,YAAY,MAAM,SAAS,GAAG;AAClD,eAAO;AAAA,MACR;AAAA,IACD;AAAA,EACD;AAEA,MAAI,KAAK,SAAS,eAAe;AAChC,WAAO,KAAK,SAAS;AAAA,EACtB;AAEA,SAAO;AACR;AAEA,SAAS,eAAe,OAAuC;AAC9D,MAAI,CAAC,MAAO,QAAO;AAOnB,MAAI,MAAM,eAAe,QAAQ,MAAM,gBAAgB,MAAM;AAC5D,YAAQ,MAAM,eAAe,MAAM,MAAM,gBAAgB;AAAA,EAC1D;AAEA,SAAO,MAAM,eAAe;AAC7B;AAEA,SAAS,mBACR,OACA,qBACC;AACD,QAAM,SAAS,eAAe,KAAK;AACnC,MAAI,UAAU,KAAM,QAAO;AAE3B,MAAI,uBAAuB,MAAM;AAChC,WAAO,gBAAgB,aAAa,MAAM,CAAC;AAAA,EAC5C;AAEA,QAAM,aAAc,SAAS,sBAAuB;AACpD,QAAM,sBACL,aAAa,KAAK,aAAa,OAAO,UAAU,WAAW,QAAQ,CAAC;AAErE,SAAO,GAAG,mBAAmB,mBAAmB,aAAa,MAAM,CAAC,MAAM,aAAa,mBAAmB,CAAC;AAC5G;AAyBA,eAAsB,UACrB,QACgB;AAGhB,MAAI,SAAsC;AAC1C,MAAI,iBAAiB;AACrB,MAAI,oBAAoB;AACxB,MAAI;AACJ,MAAI;AAEJ,QAAM,YAAY,MAAM;AACvB,QAAI,OAAQ,SAAQ,OAAO,MAAM,IAAI;AACrC,aAAS;AAAA,EACV;AAEA,QAAM,0BAA0B,CAAC,YAA2B;AAC3D,QAAI,CAAC,WAAW,qBAAsB;AACtC,2BAAuB,2BAA2B,OAAO;AAAA,EAC1D;AAEA,mBAAiB,QAAQ,QAAQ;AAChC,YAAQ,KAAK,MAAM;AAAA,MAClB,KAAK;AACJ,kBAAU;AACV,gCAAwB,WAAW,IAAI,CAAC;AACxC,gBAAQ,IAAI,MAAM,IAAI,wGAAwB,CAAC;AAC/C;AAAA,MAED,KAAK,eAAe;AACnB,0BAAkB,KAAK;AACvB,gCAAwB,WAAW,IAAI,CAAC;AACxC,cAAM,OAAO,kBAAkB,IAAI;AACnC,YAAI,QAAQ,MAAM;AACjB,4BAAkB;AAClB,8BAAoB;AAAA,QACrB;AACA;AAAA,MACD;AAAA,MAEA,KAAK;AACJ,YAAI,WAAW,aAAa;AAC3B,oBAAU;AACV,kBAAQ,OAAO,MAAM,MAAM,QAAQ,KAAK,uBAAgB,CAAC;AACzD,mBAAS;AAAA,QACV;AACA,gBAAQ,OAAO,MAAM,MAAM,QAAQ,KAAK,IAAI,CAAC;AAC7C;AAAA,MAED,KAAK;AACJ,YAAI,WAAW,QAAQ;AACtB,oBAAU;AACV,kBAAQ,OAAO,MAAM,MAAM,MAAM,KAAK,uBAAgB,CAAC;AACvD,mBAAS;AAAA,QACV;AACA,gBAAQ,OAAO,MAAM,MAAM,MAAM,KAAK,IAAI,CAAC;AAC3C;AAAA,MAED,KAAK;AACJ,kBAAU;AACV,gBAAQ;AAAA,UACP,MAAM,KAAK,KAAK,wBAAiB,KAAK,QAAQ,EAAE,IAC/C,MAAM,KAAK,IAAI,KAAK,UAAU,KAAK,KAAK,CAAC,GAAG;AAAA,QAC9C;AACA;AAAA,MAED,KAAK;AACJ,kBAAU;AACV,gBAAQ;AAAA,UACP,MAAM,MAAM,KAAK,sBAAiB,KAAK,QAAQ,GAAG,IACjD,MAAM,MAAM,KAAK,UAAU,KAAK,MAAM,CAAC;AAAA,QACzC;AACA;AAAA,MAED,KAAK;AACJ,kBAAU;AACV,gBAAQ;AAAA,UACP,MAAM,IAAI,KAAK,qBAAgB,KAAK,QAAQ,GAAG,IAC9C,MAAM,IAAI,OAAO,KAAK,KAAK,CAAC;AAAA,QAC9B;AACA;AAAA,MAED,KAAK;AACJ,kBAAU;AACV,gBAAQ,IAAI,MAAM,IAAI,KAAK,6BAAmB,GAAG,KAAK,KAAK;AAC3D;AAAA,MAED,KAAK,UAAU;AACd,kBAAU;AACV,cAAM,sBAAsB,uBACzB,MAAM,uBACN;AACH,gBAAQ;AAAA,UACP,MAAM;AAAA,YACL;AAAA,qBAAc,KAAK,YAAY,UAAO,mBAAmB,iBAAiB,mBAAmB,CAAC,GAAG,oBAAoB,cAAW,WAAW,cAAc,CAAC,KAAK,EAAE;AAAA,UAClK;AAAA,QACD;AACA;AAAA,MACD;AAAA,IACD;AAAA,EACD;AACD;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@merkie/agentic",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Batteries-included helpers for building LLM-powered apps with the Vercel AI SDK and OpenRouter — automatic cost/usage tracking and pretty stream logging out of the box.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|