@tonyclaw/llm-inspector 1.6.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/.output/nitro.json +17 -0
- package/.output/public/assets/alibaba-TTwafVwX.svg +1 -0
- package/.output/public/assets/index-B3RwBPLW.css +1 -0
- package/.output/public/assets/index-s4lwsWvq.js +97 -0
- package/.output/public/assets/main-Cp8AM0Pa.js +17 -0
- package/.output/public/assets/minimax-BPMzvuL-.jpeg +0 -0
- package/.output/public/assets/qwen-CONDcHqt.png +0 -0
- package/.output/public/assets/zhipuai-BPNAnxo-.svg +219 -0
- package/.output/server/_chunks/ssr-renderer.mjs +17 -0
- package/.output/server/_libs/@radix-ui/react-accessible-icon+[...].mjs +1 -0
- package/.output/server/_libs/@radix-ui/react-dismissable-layer+[...].mjs +210 -0
- package/.output/server/_libs/@radix-ui/react-navigation-menu+[...].mjs +1 -0
- package/.output/server/_libs/@radix-ui/react-one-time-password-field+[...].mjs +1 -0
- package/.output/server/_libs/@radix-ui/react-password-toggle-field+[...].mjs +1 -0
- package/.output/server/_libs/@radix-ui/react-use-callback-ref+[...].mjs +11 -0
- package/.output/server/_libs/@radix-ui/react-use-controllable-state+[...].mjs +69 -0
- package/.output/server/_libs/@radix-ui/react-use-effect-event+[...].mjs +1 -0
- package/.output/server/_libs/@radix-ui/react-use-escape-keydown+[...].mjs +17 -0
- package/.output/server/_libs/@radix-ui/react-use-is-hydrated+[...].mjs +1 -0
- package/.output/server/_libs/@radix-ui/react-use-layout-effect+[...].mjs +6 -0
- package/.output/server/_libs/@radix-ui/react-visually-hidden+[...].mjs +34 -0
- package/.output/server/_libs/ajv-formats.mjs +330 -0
- package/.output/server/_libs/ajv.mjs +11444 -0
- package/.output/server/_libs/aria-hidden.mjs +122 -0
- package/.output/server/_libs/atomically.mjs +152 -0
- package/.output/server/_libs/bail.mjs +8 -0
- package/.output/server/_libs/character-entities.mjs +2130 -0
- package/.output/server/_libs/class-variance-authority.mjs +44 -0
- package/.output/server/_libs/clsx.mjs +16 -0
- package/.output/server/_libs/comma-separated-tokens.mjs +10 -0
- package/.output/server/_libs/conf.mjs +635 -0
- package/.output/server/_libs/cookie-es.mjs +58 -0
- package/.output/server/_libs/core-util-is.mjs +75 -0
- package/.output/server/_libs/croner.mjs +1 -0
- package/.output/server/_libs/crossws.mjs +1 -0
- package/.output/server/_libs/debounce-fn.mjs +69 -0
- package/.output/server/_libs/decode-named-character-reference+[...].mjs +8 -0
- package/.output/server/_libs/detect-node-es.mjs +1 -0
- package/.output/server/_libs/devlop.mjs +8 -0
- package/.output/server/_libs/dot-prop.mjs +265 -0
- package/.output/server/_libs/env-paths.mjs +57 -0
- package/.output/server/_libs/estree-util-is-identifier-name.mjs +11 -0
- package/.output/server/_libs/extend.mjs +97 -0
- package/.output/server/_libs/fast-deep-equal.mjs +38 -0
- package/.output/server/_libs/fast-uri.mjs +812 -0
- package/.output/server/_libs/floating-ui__core.mjs +725 -0
- package/.output/server/_libs/floating-ui__dom.mjs +622 -0
- package/.output/server/_libs/floating-ui__react-dom.mjs +292 -0
- package/.output/server/_libs/floating-ui__utils.mjs +320 -0
- package/.output/server/_libs/get-nonce.mjs +9 -0
- package/.output/server/_libs/h3-v2.mjs +276 -0
- package/.output/server/_libs/h3.mjs +400 -0
- package/.output/server/_libs/hast-util-to-jsx-runtime.mjs +388 -0
- package/.output/server/_libs/hast-util-whitespace.mjs +10 -0
- package/.output/server/_libs/hookable.mjs +1 -0
- package/.output/server/_libs/html-url-attributes.mjs +26 -0
- package/.output/server/_libs/immediate.mjs +74 -0
- package/.output/server/_libs/inherits.mjs +50 -0
- package/.output/server/_libs/inline-style-parser.mjs +142 -0
- package/.output/server/_libs/is-plain-obj.mjs +10 -0
- package/.output/server/_libs/isarray.mjs +14 -0
- package/.output/server/_libs/isbot.mjs +20 -0
- package/.output/server/_libs/json-schema-traverse.mjs +180 -0
- package/.output/server/_libs/jszip.mjs +3049 -0
- package/.output/server/_libs/lie.mjs +273 -0
- package/.output/server/_libs/lucide-react.mjs +368 -0
- package/.output/server/_libs/mdast-util-from-markdown.mjs +717 -0
- package/.output/server/_libs/mdast-util-to-hast.mjs +710 -0
- package/.output/server/_libs/mdast-util-to-string.mjs +38 -0
- package/.output/server/_libs/micromark-core-commonmark.mjs +2259 -0
- package/.output/server/_libs/micromark-factory-destination.mjs +94 -0
- package/.output/server/_libs/micromark-factory-label.mjs +63 -0
- package/.output/server/_libs/micromark-factory-space.mjs +24 -0
- package/.output/server/_libs/micromark-factory-title.mjs +65 -0
- package/.output/server/_libs/micromark-factory-whitespace.mjs +22 -0
- package/.output/server/_libs/micromark-util-character.mjs +44 -0
- package/.output/server/_libs/micromark-util-chunked.mjs +36 -0
- package/.output/server/_libs/micromark-util-classify-character+[...].mjs +12 -0
- package/.output/server/_libs/micromark-util-combine-extensions+[...].mjs +41 -0
- package/.output/server/_libs/micromark-util-decode-numeric-character-reference+[...].mjs +19 -0
- package/.output/server/_libs/micromark-util-decode-string.mjs +21 -0
- package/.output/server/_libs/micromark-util-encode.mjs +1 -0
- package/.output/server/_libs/micromark-util-html-tag-name.mjs +69 -0
- package/.output/server/_libs/micromark-util-normalize-identifier+[...].mjs +6 -0
- package/.output/server/_libs/micromark-util-resolve-all.mjs +15 -0
- package/.output/server/_libs/micromark-util-sanitize-uri.mjs +41 -0
- package/.output/server/_libs/micromark-util-subtokenize.mjs +346 -0
- package/.output/server/_libs/micromark.mjs +906 -0
- package/.output/server/_libs/mimic-function.mjs +47 -0
- package/.output/server/_libs/ohash.mjs +1 -0
- package/.output/server/_libs/pako.mjs +4223 -0
- package/.output/server/_libs/process-nextick-args.mjs +48 -0
- package/.output/server/_libs/property-information.mjs +1209 -0
- package/.output/server/_libs/radix-ui.mjs +1 -0
- package/.output/server/_libs/radix-ui__number.mjs +6 -0
- package/.output/server/_libs/radix-ui__primitive.mjs +11 -0
- package/.output/server/_libs/radix-ui__react-accordion.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-alert-dialog.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-arrow.mjs +23 -0
- package/.output/server/_libs/radix-ui__react-aspect-ratio.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-avatar.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-checkbox.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-collapsible.mjs +144 -0
- package/.output/server/_libs/radix-ui__react-collection.mjs +69 -0
- package/.output/server/_libs/radix-ui__react-compose-refs.mjs +39 -0
- package/.output/server/_libs/radix-ui__react-context-menu.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-context.mjs +78 -0
- package/.output/server/_libs/radix-ui__react-dialog.mjs +325 -0
- package/.output/server/_libs/radix-ui__react-direction.mjs +9 -0
- package/.output/server/_libs/radix-ui__react-dropdown-menu.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-focus-guards.mjs +29 -0
- package/.output/server/_libs/radix-ui__react-focus-scope.mjs +206 -0
- package/.output/server/_libs/radix-ui__react-form.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-hover-card.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-id.mjs +14 -0
- package/.output/server/_libs/radix-ui__react-label.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-menu.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-menubar.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-popover.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-popper.mjs +286 -0
- package/.output/server/_libs/radix-ui__react-portal.mjs +16 -0
- package/.output/server/_libs/radix-ui__react-presence.mjs +128 -0
- package/.output/server/_libs/radix-ui__react-primitive.mjs +42 -0
- package/.output/server/_libs/radix-ui__react-progress.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-radio-group.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-roving-focus.mjs +224 -0
- package/.output/server/_libs/radix-ui__react-scroll-area.mjs +721 -0
- package/.output/server/_libs/radix-ui__react-select.mjs +1163 -0
- package/.output/server/_libs/radix-ui__react-separator.mjs +28 -0
- package/.output/server/_libs/radix-ui__react-slider.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-slot.mjs +99 -0
- package/.output/server/_libs/radix-ui__react-switch.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-tabs.mjs +189 -0
- package/.output/server/_libs/radix-ui__react-toast.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-toggle-group.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-toggle.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-toolbar.mjs +1 -0
- package/.output/server/_libs/radix-ui__react-tooltip.mjs +495 -0
- package/.output/server/_libs/radix-ui__react-use-previous.mjs +14 -0
- package/.output/server/_libs/radix-ui__react-use-size.mjs +39 -0
- package/.output/server/_libs/react-dom.mjs +9935 -0
- package/.output/server/_libs/react-markdown.mjs +147 -0
- package/.output/server/_libs/react-remove-scroll-bar.mjs +82 -0
- package/.output/server/_libs/react-remove-scroll.mjs +328 -0
- package/.output/server/_libs/react-style-singleton.mjs +69 -0
- package/.output/server/_libs/react.mjs +515 -0
- package/.output/server/_libs/readable-stream.mjs +1518 -0
- package/.output/server/_libs/remark-parse.mjs +19 -0
- package/.output/server/_libs/remark-rehype.mjs +21 -0
- package/.output/server/_libs/rou3.mjs +8 -0
- package/.output/server/_libs/safe-buffer.mjs +64 -0
- package/.output/server/_libs/semver.mjs +1984 -0
- package/.output/server/_libs/seroval-plugins.mjs +58 -0
- package/.output/server/_libs/seroval.mjs +1765 -0
- package/.output/server/_libs/setimmediate.mjs +1 -0
- package/.output/server/_libs/space-separated-tokens.mjs +6 -0
- package/.output/server/_libs/srvx.mjs +334 -0
- package/.output/server/_libs/stubborn-fs.mjs +91 -0
- package/.output/server/_libs/stubborn-utils.mjs +66 -0
- package/.output/server/_libs/style-to-js.mjs +72 -0
- package/.output/server/_libs/style-to-object.mjs +38 -0
- package/.output/server/_libs/tailwind-merge.mjs +3010 -0
- package/.output/server/_libs/tanstack__history.mjs +217 -0
- package/.output/server/_libs/tanstack__react-router.mjs +1480 -0
- package/.output/server/_libs/tanstack__react-store.mjs +1 -0
- package/.output/server/_libs/tanstack__react-virtual.mjs +44 -0
- package/.output/server/_libs/tanstack__router-core.mjs +4827 -0
- package/.output/server/_libs/tanstack__store.mjs +1 -0
- package/.output/server/_libs/tanstack__virtual-core.mjs +1225 -0
- package/.output/server/_libs/tiny-invariant.mjs +12 -0
- package/.output/server/_libs/tiny-warning.mjs +5 -0
- package/.output/server/_libs/trim-lines.mjs +41 -0
- package/.output/server/_libs/trough.mjs +85 -0
- package/.output/server/_libs/tslib.mjs +576 -0
- package/.output/server/_libs/ufo.mjs +54 -0
- package/.output/server/_libs/uint8array-extras.mjs +69 -0
- package/.output/server/_libs/unctx.mjs +1 -0
- package/.output/server/_libs/ungap__structured-clone.mjs +212 -0
- package/.output/server/_libs/unified.mjs +661 -0
- package/.output/server/_libs/unist-util-is.mjs +100 -0
- package/.output/server/_libs/unist-util-position.mjs +27 -0
- package/.output/server/_libs/unist-util-stringify-position.mjs +27 -0
- package/.output/server/_libs/unist-util-visit-parents.mjs +82 -0
- package/.output/server/_libs/unist-util-visit.mjs +24 -0
- package/.output/server/_libs/unstorage.mjs +1 -0
- package/.output/server/_libs/use-callback-ref.mjs +66 -0
- package/.output/server/_libs/use-sidecar.mjs +106 -0
- package/.output/server/_libs/use-sync-external-store.mjs +1 -0
- package/.output/server/_libs/util-deprecate.mjs +12 -0
- package/.output/server/_libs/vfile-message.mjs +138 -0
- package/.output/server/_libs/vfile.mjs +467 -0
- package/.output/server/_libs/when-exit.mjs +53 -0
- package/.output/server/_libs/zod.mjs +4460 -0
- package/.output/server/_ssr/index-ByCLZu7J.mjs +3061 -0
- package/.output/server/_ssr/index.mjs +1176 -0
- package/.output/server/_ssr/router-Bq_mxeNz.mjs +2872 -0
- package/.output/server/_ssr/start-HYkvq4Ni.mjs +4 -0
- package/.output/server/_tanstack-start-manifest_v-C4E0e9my.mjs +4 -0
- package/.output/server/index.mjs +393 -0
- package/README.md +196 -0
- package/package.json +91 -0
- package/src/assets/logos/alibaba.svg +1 -0
- package/src/assets/logos/anthropic.svg +1 -0
- package/src/assets/logos/deepseek.svg +1 -0
- package/src/assets/logos/minimax.jpeg +0 -0
- package/src/assets/logos/openai.svg +1 -0
- package/src/assets/logos/qwen.png +0 -0
- package/src/assets/logos/zhipuai.svg +219 -0
- package/src/cli.ts +68 -0
- package/src/components/ProxyViewer.tsx +325 -0
- package/src/components/ProxyViewerContainer.tsx +211 -0
- package/src/components/providers/ProviderCard.tsx +186 -0
- package/src/components/providers/ProviderForm.tsx +259 -0
- package/src/components/providers/ProviderLogo.tsx +111 -0
- package/src/components/providers/ProvidersPanel.tsx +259 -0
- package/src/components/providers/SettingsDialog.tsx +39 -0
- package/src/components/proxy-viewer/ConversationGroup.tsx +68 -0
- package/src/components/proxy-viewer/ConversationHeader.tsx +141 -0
- package/src/components/proxy-viewer/LogEntry.tsx +225 -0
- package/src/components/proxy-viewer/LogEntryHeader.tsx +250 -0
- package/src/components/proxy-viewer/ReplayDialog.tsx +208 -0
- package/src/components/proxy-viewer/ResponseView.tsx +161 -0
- package/src/components/proxy-viewer/StreamingChunkSequence.tsx +171 -0
- package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +139 -0
- package/src/components/proxy-viewer/formats/anthropic/ResponseView.tsx +64 -0
- package/src/components/proxy-viewer/formats/index.tsx +24 -0
- package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +80 -0
- package/src/components/proxy-viewer/index.ts +8 -0
- package/src/components/ui/badge.tsx +47 -0
- package/src/components/ui/button.tsx +47 -0
- package/src/components/ui/collapsible.tsx +21 -0
- package/src/components/ui/dialog.tsx +129 -0
- package/src/components/ui/json-viewer.tsx +464 -0
- package/src/components/ui/scroll-area.tsx +54 -0
- package/src/components/ui/select.tsx +178 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/tabs.tsx +88 -0
- package/src/components/ui/tooltip.tsx +51 -0
- package/src/index.css +11 -0
- package/src/lib/export-logs.ts +51 -0
- package/src/lib/utils.ts +22 -0
- package/src/proxy/chunkStorage.ts +118 -0
- package/src/proxy/constants.ts +36 -0
- package/src/proxy/formats/anthropic/anthropicProvider.ts +75 -0
- package/src/proxy/formats/anthropic/handler.ts +74 -0
- package/src/proxy/formats/anthropic/index.ts +14 -0
- package/src/proxy/formats/anthropic/register.ts +4 -0
- package/src/proxy/formats/anthropic/schemas.ts +217 -0
- package/src/proxy/formats/anthropic/stream.ts +167 -0
- package/src/proxy/formats/handler.ts +46 -0
- package/src/proxy/formats/index.ts +12 -0
- package/src/proxy/formats/jsonSchema.ts +24 -0
- package/src/proxy/formats/openai/alibabaProvider.ts +38 -0
- package/src/proxy/formats/openai/handler.ts +70 -0
- package/src/proxy/formats/openai/index.ts +25 -0
- package/src/proxy/formats/openai/provider.ts +50 -0
- package/src/proxy/formats/openai/register.ts +4 -0
- package/src/proxy/formats/openai/schemas.ts +150 -0
- package/src/proxy/formats/openai/stream.ts +153 -0
- package/src/proxy/formats/protocol.ts +50 -0
- package/src/proxy/formats/providerRegistry.ts +51 -0
- package/src/proxy/formats/providers/index.ts +3 -0
- package/src/proxy/formats/registry.ts +61 -0
- package/src/proxy/handler.ts +389 -0
- package/src/proxy/logIndex.ts +187 -0
- package/src/proxy/logger.ts +99 -0
- package/src/proxy/providers.ts +234 -0
- package/src/proxy/schemas.ts +160 -0
- package/src/proxy/socketTracker.ts +158 -0
- package/src/proxy/store.ts +386 -0
- package/src/router.tsx +16 -0
- package/src/routes/__root.tsx +38 -0
- package/src/routes/api/config.paths.ts +14 -0
- package/src/routes/api/health.ts +11 -0
- package/src/routes/api/logs.$id.chunks.ts +36 -0
- package/src/routes/api/logs.$id.replay.ts +262 -0
- package/src/routes/api/logs.$id.ts +22 -0
- package/src/routes/api/logs.stream.ts +64 -0
- package/src/routes/api/logs.ts +30 -0
- package/src/routes/api/models.ts +10 -0
- package/src/routes/api/providers.$providerId.ts +45 -0
- package/src/routes/api/providers.ts +37 -0
- package/src/routes/api/sessions.ts +10 -0
- package/src/routes/index.tsx +6 -0
- package/src/routes/proxy/$.ts +15 -0
- package/styles/globals.css +121 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { CapturedLog } from "../../schemas";
|
|
2
|
+
import type { ProviderProtocol } from "../protocol";
|
|
3
|
+
import { registry } from "../providers";
|
|
4
|
+
import { extractAnthropicStream } from "./stream";
|
|
5
|
+
|
|
6
|
+
const DEFAULT_UPSTREAM = "https://api.anthropic.com";
|
|
7
|
+
|
|
8
|
+
// Default routes - MiniMax is hardcoded here
|
|
9
|
+
const DEFAULT_ROUTES: Record<string, string> = {
|
|
10
|
+
MiniMax: "https://api.minimaxi.com/anthropic",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function getUpstreamForModel(model: string): string {
|
|
14
|
+
let bestPrefix = "";
|
|
15
|
+
let bestUrl = DEFAULT_UPSTREAM;
|
|
16
|
+
|
|
17
|
+
for (const [prefix, url] of Object.entries(DEFAULT_ROUTES)) {
|
|
18
|
+
if (model.startsWith(prefix) && prefix.length > bestPrefix.length) {
|
|
19
|
+
bestPrefix = prefix;
|
|
20
|
+
bestUrl = url;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return bestUrl;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Anthropic Provider Implementation
|
|
29
|
+
*/
|
|
30
|
+
export const anthropicProvider: ProviderProtocol = {
|
|
31
|
+
name: "anthropic",
|
|
32
|
+
|
|
33
|
+
matches(model: string): boolean {
|
|
34
|
+
const modelLower = model.toLowerCase();
|
|
35
|
+
|
|
36
|
+
// Don't match OpenAI models
|
|
37
|
+
if (modelLower.startsWith("openai-")) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Match Anthropic models
|
|
42
|
+
if (modelLower.includes("claude") || modelLower.includes("anthropic")) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Match known route prefixes (e.g., MiniMax)
|
|
47
|
+
for (const prefix of Object.keys(DEFAULT_ROUTES)) {
|
|
48
|
+
if (modelLower.startsWith(prefix.toLowerCase())) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Default to Anthropic for unknown models (legacy behavior)
|
|
54
|
+
return true;
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
getUpstreamBase(_isChatCompletions: boolean, providerConfig?: { baseUrl?: string }): string {
|
|
58
|
+
if (providerConfig?.baseUrl !== undefined) {
|
|
59
|
+
return providerConfig.baseUrl;
|
|
60
|
+
}
|
|
61
|
+
return DEFAULT_UPSTREAM;
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
extractStream(
|
|
65
|
+
raw: string,
|
|
66
|
+
log: CapturedLog,
|
|
67
|
+
fallbackModel?: string,
|
|
68
|
+
collectChunks?: boolean,
|
|
69
|
+
): string {
|
|
70
|
+
return extractAnthropicStream(raw, log, fallbackModel, collectChunks);
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Register the provider
|
|
75
|
+
registry.register(anthropicProvider);
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { FormatHandler, ParsedRequest } from "../handler";
|
|
2
|
+
import type { CapturedLog, TokenUsage } from "../../schemas";
|
|
3
|
+
import { AnthropicRequestSchema, AnthropicResponseSchema } from "./schemas";
|
|
4
|
+
import { extractAnthropicStream } from "./stream";
|
|
5
|
+
|
|
6
|
+
export const AnthropicFormatHandler: FormatHandler = {
|
|
7
|
+
format: "anthropic",
|
|
8
|
+
|
|
9
|
+
parseRequest(rawBody: string, headers?: Headers): ParsedRequest | null {
|
|
10
|
+
try {
|
|
11
|
+
const json: unknown = JSON.parse(rawBody);
|
|
12
|
+
const result = AnthropicRequestSchema.safeParse(json);
|
|
13
|
+
if (result.success) {
|
|
14
|
+
return {
|
|
15
|
+
model: result.data.model,
|
|
16
|
+
sessionId: result.data.metadata?.user_id ?? headers?.get("x-session-affinity") ?? null,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
} catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
extractTokens(responseBody: string): TokenUsage {
|
|
26
|
+
try {
|
|
27
|
+
const json: unknown = JSON.parse(responseBody);
|
|
28
|
+
const result = AnthropicResponseSchema.safeParse(json);
|
|
29
|
+
if (result.success) {
|
|
30
|
+
return {
|
|
31
|
+
inputTokens: result.data.usage.input_tokens,
|
|
32
|
+
outputTokens: result.data.usage.output_tokens,
|
|
33
|
+
cacheCreationInputTokens: result.data.usage.cache_creation_input_tokens ?? null,
|
|
34
|
+
cacheReadInputTokens: result.data.usage.cache_read_input_tokens ?? null,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
} catch {
|
|
38
|
+
// Fall through
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
inputTokens: null,
|
|
42
|
+
outputTokens: null,
|
|
43
|
+
cacheCreationInputTokens: null,
|
|
44
|
+
cacheReadInputTokens: null,
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
extractStream(
|
|
49
|
+
raw: string,
|
|
50
|
+
log: CapturedLog,
|
|
51
|
+
fallbackModel?: string,
|
|
52
|
+
collectChunks?: boolean,
|
|
53
|
+
): string {
|
|
54
|
+
return extractAnthropicStream(raw, log, fallbackModel, collectChunks);
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
detectFormat(rawBody: string | null): boolean {
|
|
58
|
+
if (rawBody === null) return false;
|
|
59
|
+
try {
|
|
60
|
+
const json: unknown = JSON.parse(rawBody);
|
|
61
|
+
if (typeof json === "object" && json !== null && !Array.isArray(json)) {
|
|
62
|
+
const keys = Object.keys(json);
|
|
63
|
+
if (keys.includes("model") && keys.includes("messages")) {
|
|
64
|
+
if (keys.includes("system") || keys.includes("tools")) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return false;
|
|
70
|
+
} catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Register format handler
|
|
2
|
+
import "./register";
|
|
3
|
+
|
|
4
|
+
export { AnthropicFormatHandler } from "./handler";
|
|
5
|
+
export {
|
|
6
|
+
AnthropicRequestSchema,
|
|
7
|
+
AnthropicResponseSchema,
|
|
8
|
+
SseEventSchema,
|
|
9
|
+
type AnthropicRequest,
|
|
10
|
+
type AnthropicResponse,
|
|
11
|
+
type ResponseContentBlockType,
|
|
12
|
+
} from "./schemas";
|
|
13
|
+
export { extractAnthropicStream } from "./stream";
|
|
14
|
+
export { anthropicProvider } from "./anthropicProvider";
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { JsonValueSchema } from "../jsonSchema";
|
|
3
|
+
|
|
4
|
+
const CacheControl = z.object({
|
|
5
|
+
type: z.string(),
|
|
6
|
+
ttl: z.string().optional(),
|
|
7
|
+
scope: z.string().optional(),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const TextContentBlock = z.object({
|
|
11
|
+
type: z.literal("text"),
|
|
12
|
+
text: z.string(),
|
|
13
|
+
cache_control: CacheControl.optional(),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const ThinkingContentBlock = z.object({
|
|
17
|
+
type: z.literal("thinking"),
|
|
18
|
+
thinking: z.string(),
|
|
19
|
+
signature: z.string().optional(),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const ImageSourceBlock = z.object({
|
|
23
|
+
type: z.literal("base64"),
|
|
24
|
+
media_type: z.string(),
|
|
25
|
+
data: z.string(),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const ImageContentBlock = z.object({
|
|
29
|
+
type: z.literal("image"),
|
|
30
|
+
source: ImageSourceBlock,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const ToolUseContentBlock = z.object({
|
|
34
|
+
type: z.literal("tool_use"),
|
|
35
|
+
id: z.string(),
|
|
36
|
+
name: z.string(),
|
|
37
|
+
input: z.record(z.string(), JsonValueSchema),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const ToolResultContentItem = z.discriminatedUnion("type", [TextContentBlock, ImageContentBlock]);
|
|
41
|
+
|
|
42
|
+
const ToolResultContentBlock = z.object({
|
|
43
|
+
type: z.literal("tool_result"),
|
|
44
|
+
tool_use_id: z.string().optional(),
|
|
45
|
+
content: z.union([z.string(), z.array(ToolResultContentItem)]),
|
|
46
|
+
is_error: z.boolean().optional(),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const ContentBlock = z.discriminatedUnion("type", [
|
|
50
|
+
TextContentBlock,
|
|
51
|
+
ThinkingContentBlock,
|
|
52
|
+
ImageContentBlock,
|
|
53
|
+
ToolUseContentBlock,
|
|
54
|
+
ToolResultContentBlock,
|
|
55
|
+
]);
|
|
56
|
+
|
|
57
|
+
const MessageContent = z.union([z.string(), z.array(ContentBlock)]);
|
|
58
|
+
|
|
59
|
+
const Message = z.object({
|
|
60
|
+
role: z.enum(["user", "assistant"]),
|
|
61
|
+
content: MessageContent,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const SystemBlock = z.object({
|
|
65
|
+
type: z.literal("text"),
|
|
66
|
+
text: z.string(),
|
|
67
|
+
cache_control: CacheControl.optional(),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const InputSchema = z.object({
|
|
71
|
+
type: z.string(),
|
|
72
|
+
properties: z.record(z.string(), z.record(z.string(), JsonValueSchema)).optional(),
|
|
73
|
+
required: z.array(z.string()).optional(),
|
|
74
|
+
additionalProperties: z.boolean().optional(),
|
|
75
|
+
$schema: z.string().optional(),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const ToolDefinition = z.object({
|
|
79
|
+
name: z.string(),
|
|
80
|
+
description: z.string().optional(),
|
|
81
|
+
input_schema: InputSchema.optional(),
|
|
82
|
+
cache_control: CacheControl.optional(),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const ThinkingConfig = z.discriminatedUnion("type", [
|
|
86
|
+
z.object({ type: z.literal("enabled"), budget_tokens: z.number() }),
|
|
87
|
+
z.object({ type: z.literal("disabled") }),
|
|
88
|
+
z.object({ type: z.literal("adaptive") }),
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
export const AnthropicRequestSchema = z.object({
|
|
92
|
+
model: z.string(),
|
|
93
|
+
messages: z.array(Message),
|
|
94
|
+
system: z.array(SystemBlock).optional(),
|
|
95
|
+
tools: z.array(ToolDefinition).optional(),
|
|
96
|
+
max_tokens: z.number().optional(),
|
|
97
|
+
temperature: z.number().optional(),
|
|
98
|
+
stream: z.boolean().optional(),
|
|
99
|
+
thinking: ThinkingConfig.optional(),
|
|
100
|
+
metadata: z
|
|
101
|
+
.object({
|
|
102
|
+
user_id: z.string().optional(),
|
|
103
|
+
})
|
|
104
|
+
.optional(),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const ResponseContentBlock = z.discriminatedUnion("type", [
|
|
108
|
+
TextContentBlock,
|
|
109
|
+
ThinkingContentBlock,
|
|
110
|
+
ToolUseContentBlock,
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
const ResponseUsageSchema = z.object({
|
|
114
|
+
input_tokens: z.number(),
|
|
115
|
+
output_tokens: z.number(),
|
|
116
|
+
cache_creation_input_tokens: z.number().optional(),
|
|
117
|
+
cache_read_input_tokens: z.number().optional(),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
export const AnthropicResponseSchema = z.object({
|
|
121
|
+
id: z.string(),
|
|
122
|
+
type: z.literal("message"),
|
|
123
|
+
model: z.string(),
|
|
124
|
+
role: z.literal("assistant"),
|
|
125
|
+
content: z.array(ResponseContentBlock),
|
|
126
|
+
stop_reason: z.string().nullable(),
|
|
127
|
+
stop_sequence: z.string().nullable(),
|
|
128
|
+
usage: ResponseUsageSchema,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const SseMessageStartEvent = z.object({
|
|
132
|
+
type: z.literal("message_start"),
|
|
133
|
+
message: z.object({
|
|
134
|
+
id: z.string(),
|
|
135
|
+
type: z.literal("message"),
|
|
136
|
+
model: z.string(),
|
|
137
|
+
role: z.literal("assistant"),
|
|
138
|
+
content: z.array(ResponseContentBlock),
|
|
139
|
+
stop_reason: z.null(),
|
|
140
|
+
stop_sequence: z.null(),
|
|
141
|
+
usage: z
|
|
142
|
+
.object({
|
|
143
|
+
input_tokens: z.number(),
|
|
144
|
+
cache_creation_input_tokens: z.number().optional(),
|
|
145
|
+
cache_read_input_tokens: z.number().optional(),
|
|
146
|
+
})
|
|
147
|
+
.passthrough(),
|
|
148
|
+
}),
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const SseContentBlockStartEvent = z.object({
|
|
152
|
+
type: z.literal("content_block_start"),
|
|
153
|
+
index: z.number(),
|
|
154
|
+
content_block: ResponseContentBlock,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const SseDeltaBlock = z.discriminatedUnion("type", [
|
|
158
|
+
z.object({ type: z.literal("text_delta"), text: z.string() }),
|
|
159
|
+
z.object({ type: z.literal("input_json_delta"), partial_json: z.string() }),
|
|
160
|
+
z.object({ type: z.literal("thinking_delta"), thinking: z.string() }),
|
|
161
|
+
z.object({ type: z.literal("signature_delta"), signature: z.string() }),
|
|
162
|
+
]);
|
|
163
|
+
|
|
164
|
+
const SseContentBlockDeltaEvent = z.object({
|
|
165
|
+
type: z.literal("content_block_delta"),
|
|
166
|
+
index: z.number(),
|
|
167
|
+
delta: SseDeltaBlock,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const SseContentBlockStopEvent = z.object({
|
|
171
|
+
type: z.literal("content_block_stop"),
|
|
172
|
+
index: z.number(),
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const SseMessageDeltaEvent = z.object({
|
|
176
|
+
type: z.literal("message_delta"),
|
|
177
|
+
delta: z.object({
|
|
178
|
+
stop_reason: z.string().nullable(),
|
|
179
|
+
stop_sequence: z.string().nullable().optional(),
|
|
180
|
+
}),
|
|
181
|
+
usage: z
|
|
182
|
+
.object({
|
|
183
|
+
output_tokens: z.number(),
|
|
184
|
+
input_tokens: z.number().optional(),
|
|
185
|
+
cache_creation_input_tokens: z.number().optional(),
|
|
186
|
+
cache_read_input_tokens: z.number().optional(),
|
|
187
|
+
})
|
|
188
|
+
.passthrough(),
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const SseMessageStopEvent = z.object({
|
|
192
|
+
type: z.literal("message_stop"),
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const SsePingEvent = z.object({
|
|
196
|
+
type: z.literal("ping"),
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
export const SseEventSchema = z.discriminatedUnion("type", [
|
|
200
|
+
SseMessageStartEvent,
|
|
201
|
+
SseContentBlockStartEvent,
|
|
202
|
+
SseContentBlockDeltaEvent,
|
|
203
|
+
SseContentBlockStopEvent,
|
|
204
|
+
SseMessageDeltaEvent,
|
|
205
|
+
SseMessageStopEvent,
|
|
206
|
+
SsePingEvent,
|
|
207
|
+
]);
|
|
208
|
+
|
|
209
|
+
export type AnthropicRequest = z.infer<typeof AnthropicRequestSchema>;
|
|
210
|
+
export type AnthropicResponse = z.infer<typeof AnthropicResponseSchema>;
|
|
211
|
+
export type ResponseContentBlockType = z.infer<typeof ResponseContentBlock>;
|
|
212
|
+
|
|
213
|
+
// Backward-compatible aliases (originally named Inspector*)
|
|
214
|
+
export const InspectorRequestSchema = AnthropicRequestSchema;
|
|
215
|
+
export const InspectorResponseSchema = AnthropicResponseSchema;
|
|
216
|
+
export type InspectorRequest = AnthropicRequest;
|
|
217
|
+
export type InspectorResponse = AnthropicResponse;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type { CapturedLog, JsonValue } from "../../schemas";
|
|
2
|
+
import { JsonValueSchema } from "../jsonSchema";
|
|
3
|
+
import { SseEventSchema } from "./schemas";
|
|
4
|
+
|
|
5
|
+
type StreamTextBlock = { type: "text"; text: string };
|
|
6
|
+
type StreamThinkingBlock = { type: "thinking"; thinking: string; signature: string };
|
|
7
|
+
type StreamToolUseBlock = { type: "tool_use"; id: string; name: string; inputJson: string };
|
|
8
|
+
type StreamBlock = StreamTextBlock | StreamThinkingBlock | StreamToolUseBlock;
|
|
9
|
+
|
|
10
|
+
function parseInputJson(json: string): Record<string, unknown> {
|
|
11
|
+
if (json === "") return {};
|
|
12
|
+
try {
|
|
13
|
+
const parsed: unknown = JSON.parse(json);
|
|
14
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
15
|
+
return { ...parsed };
|
|
16
|
+
}
|
|
17
|
+
return {};
|
|
18
|
+
} catch {
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function finalizeBlock(block: StreamBlock): Record<string, unknown> {
|
|
24
|
+
switch (block.type) {
|
|
25
|
+
case "text":
|
|
26
|
+
return { type: "text", text: block.text };
|
|
27
|
+
case "thinking": {
|
|
28
|
+
const out: Record<string, unknown> = { type: "thinking", thinking: block.thinking };
|
|
29
|
+
if (block.signature !== "") out.signature = block.signature;
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
case "tool_use":
|
|
33
|
+
return {
|
|
34
|
+
type: "tool_use",
|
|
35
|
+
id: block.id,
|
|
36
|
+
name: block.name,
|
|
37
|
+
input: parseInputJson(block.inputJson),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Extract Anthropic streaming response and return normalized JSON.
|
|
44
|
+
* Optionally collects SSE chunks into log.streamingChunks for debugging/display.
|
|
45
|
+
*/
|
|
46
|
+
export function extractAnthropicStream(
|
|
47
|
+
raw: string,
|
|
48
|
+
log: CapturedLog,
|
|
49
|
+
fallbackModel?: string,
|
|
50
|
+
collectChunks?: boolean,
|
|
51
|
+
): string {
|
|
52
|
+
const blocks = new Map<number, StreamBlock>();
|
|
53
|
+
let id = "";
|
|
54
|
+
let model = "";
|
|
55
|
+
let stopReason: string | null = null;
|
|
56
|
+
let stopSequence: string | null = null;
|
|
57
|
+
let inputTokens = 0;
|
|
58
|
+
let outputTokens = 0;
|
|
59
|
+
|
|
60
|
+
const MAX_CHUNKS = 1000;
|
|
61
|
+
let chunkIndex = 0;
|
|
62
|
+
let streamStartMs = 0;
|
|
63
|
+
const chunks: Array<{ index: number; timestamp: number; type: string; data: JsonValue }> = [];
|
|
64
|
+
|
|
65
|
+
for (const line of raw.split("\n")) {
|
|
66
|
+
if (!line.startsWith("data: ")) continue;
|
|
67
|
+
try {
|
|
68
|
+
const json: unknown = JSON.parse(line.slice(6));
|
|
69
|
+
const parsed = SseEventSchema.safeParse(json);
|
|
70
|
+
if (!parsed.success) continue;
|
|
71
|
+
const data = parsed.data;
|
|
72
|
+
|
|
73
|
+
if (chunkIndex === 0) streamStartMs = Date.now();
|
|
74
|
+
|
|
75
|
+
if (collectChunks === true && chunks.length < MAX_CHUNKS) {
|
|
76
|
+
chunks.push({
|
|
77
|
+
index: chunkIndex,
|
|
78
|
+
timestamp: Date.now() - streamStartMs,
|
|
79
|
+
type: data.type,
|
|
80
|
+
data: JsonValueSchema.parse(data),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
chunkIndex++;
|
|
84
|
+
|
|
85
|
+
switch (data.type) {
|
|
86
|
+
case "message_start":
|
|
87
|
+
id = data.message.id;
|
|
88
|
+
model = data.message.model;
|
|
89
|
+
inputTokens = data.message.usage.input_tokens;
|
|
90
|
+
log.inputTokens = inputTokens;
|
|
91
|
+
log.cacheCreationInputTokens = data.message.usage.cache_creation_input_tokens ?? null;
|
|
92
|
+
log.cacheReadInputTokens = data.message.usage.cache_read_input_tokens ?? null;
|
|
93
|
+
if (log.model === null) log.model = model;
|
|
94
|
+
break;
|
|
95
|
+
case "content_block_start": {
|
|
96
|
+
const cb = data.content_block;
|
|
97
|
+
if (cb.type === "text") {
|
|
98
|
+
blocks.set(data.index, { type: "text", text: cb.text ?? "" });
|
|
99
|
+
} else if (cb.type === "thinking") {
|
|
100
|
+
blocks.set(data.index, {
|
|
101
|
+
type: "thinking",
|
|
102
|
+
thinking: cb.thinking ?? "",
|
|
103
|
+
signature: cb.signature ?? "",
|
|
104
|
+
});
|
|
105
|
+
} else {
|
|
106
|
+
blocks.set(data.index, {
|
|
107
|
+
type: "tool_use",
|
|
108
|
+
id: cb.id,
|
|
109
|
+
name: cb.name,
|
|
110
|
+
inputJson: "",
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
case "content_block_delta": {
|
|
116
|
+
const block = blocks.get(data.index);
|
|
117
|
+
if (block === undefined) break;
|
|
118
|
+
const delta = data.delta;
|
|
119
|
+
if (delta.type === "text_delta" && block.type === "text") {
|
|
120
|
+
block.text += delta.text;
|
|
121
|
+
} else if (delta.type === "thinking_delta" && block.type === "thinking") {
|
|
122
|
+
block.thinking += delta.thinking;
|
|
123
|
+
} else if (delta.type === "signature_delta" && block.type === "thinking") {
|
|
124
|
+
block.signature += delta.signature;
|
|
125
|
+
} else if (delta.type === "input_json_delta" && block.type === "tool_use") {
|
|
126
|
+
block.inputJson += delta.partial_json;
|
|
127
|
+
}
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
case "message_delta":
|
|
131
|
+
stopReason = data.delta.stop_reason;
|
|
132
|
+
stopSequence = data.delta.stop_sequence ?? null;
|
|
133
|
+
outputTokens = data.usage.output_tokens;
|
|
134
|
+
log.outputTokens = outputTokens;
|
|
135
|
+
break;
|
|
136
|
+
case "content_block_stop":
|
|
137
|
+
case "message_stop":
|
|
138
|
+
case "ping":
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
} catch {
|
|
142
|
+
// non-JSON SSE line, skip
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (collectChunks === true) {
|
|
147
|
+
log.streamingChunks = {
|
|
148
|
+
chunks,
|
|
149
|
+
truncated: chunkIndex > MAX_CHUNKS,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const orderedContent = [...blocks.entries()]
|
|
154
|
+
.sort(([a], [b]) => a - b)
|
|
155
|
+
.map(([, block]) => finalizeBlock(block));
|
|
156
|
+
|
|
157
|
+
return JSON.stringify({
|
|
158
|
+
id,
|
|
159
|
+
type: "message",
|
|
160
|
+
model: model !== "" ? model : (fallbackModel ?? ""),
|
|
161
|
+
role: "assistant",
|
|
162
|
+
content: orderedContent,
|
|
163
|
+
stop_reason: stopReason,
|
|
164
|
+
stop_sequence: stopSequence,
|
|
165
|
+
usage: { input_tokens: inputTokens, output_tokens: outputTokens },
|
|
166
|
+
});
|
|
167
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { CapturedLog, RequestFormat, TokenUsage } from "../schemas";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Structured result of parsing a raw request body.
|
|
5
|
+
*/
|
|
6
|
+
export type ParsedRequest = {
|
|
7
|
+
model: string | null;
|
|
8
|
+
sessionId: string | null;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* FormatHandler defines the contract for all API format implementations.
|
|
13
|
+
*
|
|
14
|
+
* Each API format (Anthropic, OpenAI) provides its own handler that encapsulates
|
|
15
|
+
* request parsing, response parsing, SSE stream extraction, and token extraction.
|
|
16
|
+
* This replaces the scattered if/else format branching previously in handler.ts.
|
|
17
|
+
*/
|
|
18
|
+
export type FormatHandler = {
|
|
19
|
+
/** The format identifier */
|
|
20
|
+
readonly format: RequestFormat;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Parse a raw request body to extract model name and metadata.
|
|
24
|
+
*/
|
|
25
|
+
parseRequest(rawBody: string, headers?: Headers): ParsedRequest | null;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Extract token usage from a non-streaming response body.
|
|
29
|
+
*/
|
|
30
|
+
extractTokens(responseBody: string): TokenUsage;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Extract structured content from a streaming SSE response.
|
|
34
|
+
*/
|
|
35
|
+
extractStream(
|
|
36
|
+
raw: string,
|
|
37
|
+
log: CapturedLog,
|
|
38
|
+
fallbackModel?: string,
|
|
39
|
+
collectChunks?: boolean,
|
|
40
|
+
): string;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Detect if a raw request body uses this format.
|
|
44
|
+
*/
|
|
45
|
+
detectFormat(rawBody: string | null): boolean;
|
|
46
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Import submodules to trigger handler registration
|
|
2
|
+
import "./anthropic/index";
|
|
3
|
+
import "./openai/index";
|
|
4
|
+
|
|
5
|
+
// Import providers to trigger self-registration
|
|
6
|
+
import "./anthropic/anthropicProvider";
|
|
7
|
+
import "./openai/provider";
|
|
8
|
+
import "./openai/alibabaProvider";
|
|
9
|
+
|
|
10
|
+
export type { FormatHandler, ParsedRequest } from "./handler";
|
|
11
|
+
export { formatRegistry, formatForPath } from "./registry";
|
|
12
|
+
export { registry } from "./providers";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared JSON value type for streaming chunks.
|
|
5
|
+
* Defined in a separate file to avoid circular dependencies.
|
|
6
|
+
*/
|
|
7
|
+
export type JsonValue =
|
|
8
|
+
| string
|
|
9
|
+
| number
|
|
10
|
+
| boolean
|
|
11
|
+
| null
|
|
12
|
+
| JsonValue[]
|
|
13
|
+
| { [key: string]: JsonValue };
|
|
14
|
+
|
|
15
|
+
export const JsonValueSchema: z.ZodType<JsonValue> = z.lazy(() =>
|
|
16
|
+
z.union([
|
|
17
|
+
z.string(),
|
|
18
|
+
z.number(),
|
|
19
|
+
z.boolean(),
|
|
20
|
+
z.null(),
|
|
21
|
+
z.array(JsonValueSchema),
|
|
22
|
+
z.record(z.string(), JsonValueSchema),
|
|
23
|
+
]),
|
|
24
|
+
);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { CapturedLog } from "../../schemas";
|
|
2
|
+
import type { ProviderProtocol } from "../protocol";
|
|
3
|
+
import { registry } from "../providers";
|
|
4
|
+
import { extractOpenAIStream } from "./stream";
|
|
5
|
+
|
|
6
|
+
const DEFAULT_ALIBABA_UPSTREAM = "https://dashscope.aliyuncs.com/compatible-mode/v1";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Alibaba Provider Implementation (DashScope)
|
|
10
|
+
*/
|
|
11
|
+
export const alibabaProvider: ProviderProtocol = {
|
|
12
|
+
name: "alibaba",
|
|
13
|
+
|
|
14
|
+
matches(model: string): boolean {
|
|
15
|
+
// Normalize: lowercase, replace whitespace with hyphens
|
|
16
|
+
const m = model.toLowerCase().replace(/\s+/g, "-");
|
|
17
|
+
// Match glm-5* and glm-5.1* models
|
|
18
|
+
if (m.startsWith("glm-5")) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
// Match qwen models (qwen3.6-plus, qwen3.7-max, Qwen3.6 Plus, etc.)
|
|
22
|
+
if (m.startsWith("qwen")) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
getUpstreamBase(_isChatCompletions: boolean, _providerConfig?: { baseUrl?: string }): string {
|
|
29
|
+
return DEFAULT_ALIBABA_UPSTREAM;
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
extractStream(raw: string, log: CapturedLog, fallbackModel?: string): string {
|
|
33
|
+
return extractOpenAIStream(raw, log, fallbackModel);
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Register the provider
|
|
38
|
+
registry.register(alibabaProvider);
|