@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.
Files changed (286) hide show
  1. package/.output/nitro.json +17 -0
  2. package/.output/public/assets/alibaba-TTwafVwX.svg +1 -0
  3. package/.output/public/assets/index-B3RwBPLW.css +1 -0
  4. package/.output/public/assets/index-s4lwsWvq.js +97 -0
  5. package/.output/public/assets/main-Cp8AM0Pa.js +17 -0
  6. package/.output/public/assets/minimax-BPMzvuL-.jpeg +0 -0
  7. package/.output/public/assets/qwen-CONDcHqt.png +0 -0
  8. package/.output/public/assets/zhipuai-BPNAnxo-.svg +219 -0
  9. package/.output/server/_chunks/ssr-renderer.mjs +17 -0
  10. package/.output/server/_libs/@radix-ui/react-accessible-icon+[...].mjs +1 -0
  11. package/.output/server/_libs/@radix-ui/react-dismissable-layer+[...].mjs +210 -0
  12. package/.output/server/_libs/@radix-ui/react-navigation-menu+[...].mjs +1 -0
  13. package/.output/server/_libs/@radix-ui/react-one-time-password-field+[...].mjs +1 -0
  14. package/.output/server/_libs/@radix-ui/react-password-toggle-field+[...].mjs +1 -0
  15. package/.output/server/_libs/@radix-ui/react-use-callback-ref+[...].mjs +11 -0
  16. package/.output/server/_libs/@radix-ui/react-use-controllable-state+[...].mjs +69 -0
  17. package/.output/server/_libs/@radix-ui/react-use-effect-event+[...].mjs +1 -0
  18. package/.output/server/_libs/@radix-ui/react-use-escape-keydown+[...].mjs +17 -0
  19. package/.output/server/_libs/@radix-ui/react-use-is-hydrated+[...].mjs +1 -0
  20. package/.output/server/_libs/@radix-ui/react-use-layout-effect+[...].mjs +6 -0
  21. package/.output/server/_libs/@radix-ui/react-visually-hidden+[...].mjs +34 -0
  22. package/.output/server/_libs/ajv-formats.mjs +330 -0
  23. package/.output/server/_libs/ajv.mjs +11444 -0
  24. package/.output/server/_libs/aria-hidden.mjs +122 -0
  25. package/.output/server/_libs/atomically.mjs +152 -0
  26. package/.output/server/_libs/bail.mjs +8 -0
  27. package/.output/server/_libs/character-entities.mjs +2130 -0
  28. package/.output/server/_libs/class-variance-authority.mjs +44 -0
  29. package/.output/server/_libs/clsx.mjs +16 -0
  30. package/.output/server/_libs/comma-separated-tokens.mjs +10 -0
  31. package/.output/server/_libs/conf.mjs +635 -0
  32. package/.output/server/_libs/cookie-es.mjs +58 -0
  33. package/.output/server/_libs/core-util-is.mjs +75 -0
  34. package/.output/server/_libs/croner.mjs +1 -0
  35. package/.output/server/_libs/crossws.mjs +1 -0
  36. package/.output/server/_libs/debounce-fn.mjs +69 -0
  37. package/.output/server/_libs/decode-named-character-reference+[...].mjs +8 -0
  38. package/.output/server/_libs/detect-node-es.mjs +1 -0
  39. package/.output/server/_libs/devlop.mjs +8 -0
  40. package/.output/server/_libs/dot-prop.mjs +265 -0
  41. package/.output/server/_libs/env-paths.mjs +57 -0
  42. package/.output/server/_libs/estree-util-is-identifier-name.mjs +11 -0
  43. package/.output/server/_libs/extend.mjs +97 -0
  44. package/.output/server/_libs/fast-deep-equal.mjs +38 -0
  45. package/.output/server/_libs/fast-uri.mjs +812 -0
  46. package/.output/server/_libs/floating-ui__core.mjs +725 -0
  47. package/.output/server/_libs/floating-ui__dom.mjs +622 -0
  48. package/.output/server/_libs/floating-ui__react-dom.mjs +292 -0
  49. package/.output/server/_libs/floating-ui__utils.mjs +320 -0
  50. package/.output/server/_libs/get-nonce.mjs +9 -0
  51. package/.output/server/_libs/h3-v2.mjs +276 -0
  52. package/.output/server/_libs/h3.mjs +400 -0
  53. package/.output/server/_libs/hast-util-to-jsx-runtime.mjs +388 -0
  54. package/.output/server/_libs/hast-util-whitespace.mjs +10 -0
  55. package/.output/server/_libs/hookable.mjs +1 -0
  56. package/.output/server/_libs/html-url-attributes.mjs +26 -0
  57. package/.output/server/_libs/immediate.mjs +74 -0
  58. package/.output/server/_libs/inherits.mjs +50 -0
  59. package/.output/server/_libs/inline-style-parser.mjs +142 -0
  60. package/.output/server/_libs/is-plain-obj.mjs +10 -0
  61. package/.output/server/_libs/isarray.mjs +14 -0
  62. package/.output/server/_libs/isbot.mjs +20 -0
  63. package/.output/server/_libs/json-schema-traverse.mjs +180 -0
  64. package/.output/server/_libs/jszip.mjs +3049 -0
  65. package/.output/server/_libs/lie.mjs +273 -0
  66. package/.output/server/_libs/lucide-react.mjs +368 -0
  67. package/.output/server/_libs/mdast-util-from-markdown.mjs +717 -0
  68. package/.output/server/_libs/mdast-util-to-hast.mjs +710 -0
  69. package/.output/server/_libs/mdast-util-to-string.mjs +38 -0
  70. package/.output/server/_libs/micromark-core-commonmark.mjs +2259 -0
  71. package/.output/server/_libs/micromark-factory-destination.mjs +94 -0
  72. package/.output/server/_libs/micromark-factory-label.mjs +63 -0
  73. package/.output/server/_libs/micromark-factory-space.mjs +24 -0
  74. package/.output/server/_libs/micromark-factory-title.mjs +65 -0
  75. package/.output/server/_libs/micromark-factory-whitespace.mjs +22 -0
  76. package/.output/server/_libs/micromark-util-character.mjs +44 -0
  77. package/.output/server/_libs/micromark-util-chunked.mjs +36 -0
  78. package/.output/server/_libs/micromark-util-classify-character+[...].mjs +12 -0
  79. package/.output/server/_libs/micromark-util-combine-extensions+[...].mjs +41 -0
  80. package/.output/server/_libs/micromark-util-decode-numeric-character-reference+[...].mjs +19 -0
  81. package/.output/server/_libs/micromark-util-decode-string.mjs +21 -0
  82. package/.output/server/_libs/micromark-util-encode.mjs +1 -0
  83. package/.output/server/_libs/micromark-util-html-tag-name.mjs +69 -0
  84. package/.output/server/_libs/micromark-util-normalize-identifier+[...].mjs +6 -0
  85. package/.output/server/_libs/micromark-util-resolve-all.mjs +15 -0
  86. package/.output/server/_libs/micromark-util-sanitize-uri.mjs +41 -0
  87. package/.output/server/_libs/micromark-util-subtokenize.mjs +346 -0
  88. package/.output/server/_libs/micromark.mjs +906 -0
  89. package/.output/server/_libs/mimic-function.mjs +47 -0
  90. package/.output/server/_libs/ohash.mjs +1 -0
  91. package/.output/server/_libs/pako.mjs +4223 -0
  92. package/.output/server/_libs/process-nextick-args.mjs +48 -0
  93. package/.output/server/_libs/property-information.mjs +1209 -0
  94. package/.output/server/_libs/radix-ui.mjs +1 -0
  95. package/.output/server/_libs/radix-ui__number.mjs +6 -0
  96. package/.output/server/_libs/radix-ui__primitive.mjs +11 -0
  97. package/.output/server/_libs/radix-ui__react-accordion.mjs +1 -0
  98. package/.output/server/_libs/radix-ui__react-alert-dialog.mjs +1 -0
  99. package/.output/server/_libs/radix-ui__react-arrow.mjs +23 -0
  100. package/.output/server/_libs/radix-ui__react-aspect-ratio.mjs +1 -0
  101. package/.output/server/_libs/radix-ui__react-avatar.mjs +1 -0
  102. package/.output/server/_libs/radix-ui__react-checkbox.mjs +1 -0
  103. package/.output/server/_libs/radix-ui__react-collapsible.mjs +144 -0
  104. package/.output/server/_libs/radix-ui__react-collection.mjs +69 -0
  105. package/.output/server/_libs/radix-ui__react-compose-refs.mjs +39 -0
  106. package/.output/server/_libs/radix-ui__react-context-menu.mjs +1 -0
  107. package/.output/server/_libs/radix-ui__react-context.mjs +78 -0
  108. package/.output/server/_libs/radix-ui__react-dialog.mjs +325 -0
  109. package/.output/server/_libs/radix-ui__react-direction.mjs +9 -0
  110. package/.output/server/_libs/radix-ui__react-dropdown-menu.mjs +1 -0
  111. package/.output/server/_libs/radix-ui__react-focus-guards.mjs +29 -0
  112. package/.output/server/_libs/radix-ui__react-focus-scope.mjs +206 -0
  113. package/.output/server/_libs/radix-ui__react-form.mjs +1 -0
  114. package/.output/server/_libs/radix-ui__react-hover-card.mjs +1 -0
  115. package/.output/server/_libs/radix-ui__react-id.mjs +14 -0
  116. package/.output/server/_libs/radix-ui__react-label.mjs +1 -0
  117. package/.output/server/_libs/radix-ui__react-menu.mjs +1 -0
  118. package/.output/server/_libs/radix-ui__react-menubar.mjs +1 -0
  119. package/.output/server/_libs/radix-ui__react-popover.mjs +1 -0
  120. package/.output/server/_libs/radix-ui__react-popper.mjs +286 -0
  121. package/.output/server/_libs/radix-ui__react-portal.mjs +16 -0
  122. package/.output/server/_libs/radix-ui__react-presence.mjs +128 -0
  123. package/.output/server/_libs/radix-ui__react-primitive.mjs +42 -0
  124. package/.output/server/_libs/radix-ui__react-progress.mjs +1 -0
  125. package/.output/server/_libs/radix-ui__react-radio-group.mjs +1 -0
  126. package/.output/server/_libs/radix-ui__react-roving-focus.mjs +224 -0
  127. package/.output/server/_libs/radix-ui__react-scroll-area.mjs +721 -0
  128. package/.output/server/_libs/radix-ui__react-select.mjs +1163 -0
  129. package/.output/server/_libs/radix-ui__react-separator.mjs +28 -0
  130. package/.output/server/_libs/radix-ui__react-slider.mjs +1 -0
  131. package/.output/server/_libs/radix-ui__react-slot.mjs +99 -0
  132. package/.output/server/_libs/radix-ui__react-switch.mjs +1 -0
  133. package/.output/server/_libs/radix-ui__react-tabs.mjs +189 -0
  134. package/.output/server/_libs/radix-ui__react-toast.mjs +1 -0
  135. package/.output/server/_libs/radix-ui__react-toggle-group.mjs +1 -0
  136. package/.output/server/_libs/radix-ui__react-toggle.mjs +1 -0
  137. package/.output/server/_libs/radix-ui__react-toolbar.mjs +1 -0
  138. package/.output/server/_libs/radix-ui__react-tooltip.mjs +495 -0
  139. package/.output/server/_libs/radix-ui__react-use-previous.mjs +14 -0
  140. package/.output/server/_libs/radix-ui__react-use-size.mjs +39 -0
  141. package/.output/server/_libs/react-dom.mjs +9935 -0
  142. package/.output/server/_libs/react-markdown.mjs +147 -0
  143. package/.output/server/_libs/react-remove-scroll-bar.mjs +82 -0
  144. package/.output/server/_libs/react-remove-scroll.mjs +328 -0
  145. package/.output/server/_libs/react-style-singleton.mjs +69 -0
  146. package/.output/server/_libs/react.mjs +515 -0
  147. package/.output/server/_libs/readable-stream.mjs +1518 -0
  148. package/.output/server/_libs/remark-parse.mjs +19 -0
  149. package/.output/server/_libs/remark-rehype.mjs +21 -0
  150. package/.output/server/_libs/rou3.mjs +8 -0
  151. package/.output/server/_libs/safe-buffer.mjs +64 -0
  152. package/.output/server/_libs/semver.mjs +1984 -0
  153. package/.output/server/_libs/seroval-plugins.mjs +58 -0
  154. package/.output/server/_libs/seroval.mjs +1765 -0
  155. package/.output/server/_libs/setimmediate.mjs +1 -0
  156. package/.output/server/_libs/space-separated-tokens.mjs +6 -0
  157. package/.output/server/_libs/srvx.mjs +334 -0
  158. package/.output/server/_libs/stubborn-fs.mjs +91 -0
  159. package/.output/server/_libs/stubborn-utils.mjs +66 -0
  160. package/.output/server/_libs/style-to-js.mjs +72 -0
  161. package/.output/server/_libs/style-to-object.mjs +38 -0
  162. package/.output/server/_libs/tailwind-merge.mjs +3010 -0
  163. package/.output/server/_libs/tanstack__history.mjs +217 -0
  164. package/.output/server/_libs/tanstack__react-router.mjs +1480 -0
  165. package/.output/server/_libs/tanstack__react-store.mjs +1 -0
  166. package/.output/server/_libs/tanstack__react-virtual.mjs +44 -0
  167. package/.output/server/_libs/tanstack__router-core.mjs +4827 -0
  168. package/.output/server/_libs/tanstack__store.mjs +1 -0
  169. package/.output/server/_libs/tanstack__virtual-core.mjs +1225 -0
  170. package/.output/server/_libs/tiny-invariant.mjs +12 -0
  171. package/.output/server/_libs/tiny-warning.mjs +5 -0
  172. package/.output/server/_libs/trim-lines.mjs +41 -0
  173. package/.output/server/_libs/trough.mjs +85 -0
  174. package/.output/server/_libs/tslib.mjs +576 -0
  175. package/.output/server/_libs/ufo.mjs +54 -0
  176. package/.output/server/_libs/uint8array-extras.mjs +69 -0
  177. package/.output/server/_libs/unctx.mjs +1 -0
  178. package/.output/server/_libs/ungap__structured-clone.mjs +212 -0
  179. package/.output/server/_libs/unified.mjs +661 -0
  180. package/.output/server/_libs/unist-util-is.mjs +100 -0
  181. package/.output/server/_libs/unist-util-position.mjs +27 -0
  182. package/.output/server/_libs/unist-util-stringify-position.mjs +27 -0
  183. package/.output/server/_libs/unist-util-visit-parents.mjs +82 -0
  184. package/.output/server/_libs/unist-util-visit.mjs +24 -0
  185. package/.output/server/_libs/unstorage.mjs +1 -0
  186. package/.output/server/_libs/use-callback-ref.mjs +66 -0
  187. package/.output/server/_libs/use-sidecar.mjs +106 -0
  188. package/.output/server/_libs/use-sync-external-store.mjs +1 -0
  189. package/.output/server/_libs/util-deprecate.mjs +12 -0
  190. package/.output/server/_libs/vfile-message.mjs +138 -0
  191. package/.output/server/_libs/vfile.mjs +467 -0
  192. package/.output/server/_libs/when-exit.mjs +53 -0
  193. package/.output/server/_libs/zod.mjs +4460 -0
  194. package/.output/server/_ssr/index-ByCLZu7J.mjs +3061 -0
  195. package/.output/server/_ssr/index.mjs +1176 -0
  196. package/.output/server/_ssr/router-Bq_mxeNz.mjs +2872 -0
  197. package/.output/server/_ssr/start-HYkvq4Ni.mjs +4 -0
  198. package/.output/server/_tanstack-start-manifest_v-C4E0e9my.mjs +4 -0
  199. package/.output/server/index.mjs +393 -0
  200. package/README.md +196 -0
  201. package/package.json +91 -0
  202. package/src/assets/logos/alibaba.svg +1 -0
  203. package/src/assets/logos/anthropic.svg +1 -0
  204. package/src/assets/logos/deepseek.svg +1 -0
  205. package/src/assets/logos/minimax.jpeg +0 -0
  206. package/src/assets/logos/openai.svg +1 -0
  207. package/src/assets/logos/qwen.png +0 -0
  208. package/src/assets/logos/zhipuai.svg +219 -0
  209. package/src/cli.ts +68 -0
  210. package/src/components/ProxyViewer.tsx +325 -0
  211. package/src/components/ProxyViewerContainer.tsx +211 -0
  212. package/src/components/providers/ProviderCard.tsx +186 -0
  213. package/src/components/providers/ProviderForm.tsx +259 -0
  214. package/src/components/providers/ProviderLogo.tsx +111 -0
  215. package/src/components/providers/ProvidersPanel.tsx +259 -0
  216. package/src/components/providers/SettingsDialog.tsx +39 -0
  217. package/src/components/proxy-viewer/ConversationGroup.tsx +68 -0
  218. package/src/components/proxy-viewer/ConversationHeader.tsx +141 -0
  219. package/src/components/proxy-viewer/LogEntry.tsx +225 -0
  220. package/src/components/proxy-viewer/LogEntryHeader.tsx +250 -0
  221. package/src/components/proxy-viewer/ReplayDialog.tsx +208 -0
  222. package/src/components/proxy-viewer/ResponseView.tsx +161 -0
  223. package/src/components/proxy-viewer/StreamingChunkSequence.tsx +171 -0
  224. package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +139 -0
  225. package/src/components/proxy-viewer/formats/anthropic/ResponseView.tsx +64 -0
  226. package/src/components/proxy-viewer/formats/index.tsx +24 -0
  227. package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +80 -0
  228. package/src/components/proxy-viewer/index.ts +8 -0
  229. package/src/components/ui/badge.tsx +47 -0
  230. package/src/components/ui/button.tsx +47 -0
  231. package/src/components/ui/collapsible.tsx +21 -0
  232. package/src/components/ui/dialog.tsx +129 -0
  233. package/src/components/ui/json-viewer.tsx +464 -0
  234. package/src/components/ui/scroll-area.tsx +54 -0
  235. package/src/components/ui/select.tsx +178 -0
  236. package/src/components/ui/separator.tsx +28 -0
  237. package/src/components/ui/tabs.tsx +88 -0
  238. package/src/components/ui/tooltip.tsx +51 -0
  239. package/src/index.css +11 -0
  240. package/src/lib/export-logs.ts +51 -0
  241. package/src/lib/utils.ts +22 -0
  242. package/src/proxy/chunkStorage.ts +118 -0
  243. package/src/proxy/constants.ts +36 -0
  244. package/src/proxy/formats/anthropic/anthropicProvider.ts +75 -0
  245. package/src/proxy/formats/anthropic/handler.ts +74 -0
  246. package/src/proxy/formats/anthropic/index.ts +14 -0
  247. package/src/proxy/formats/anthropic/register.ts +4 -0
  248. package/src/proxy/formats/anthropic/schemas.ts +217 -0
  249. package/src/proxy/formats/anthropic/stream.ts +167 -0
  250. package/src/proxy/formats/handler.ts +46 -0
  251. package/src/proxy/formats/index.ts +12 -0
  252. package/src/proxy/formats/jsonSchema.ts +24 -0
  253. package/src/proxy/formats/openai/alibabaProvider.ts +38 -0
  254. package/src/proxy/formats/openai/handler.ts +70 -0
  255. package/src/proxy/formats/openai/index.ts +25 -0
  256. package/src/proxy/formats/openai/provider.ts +50 -0
  257. package/src/proxy/formats/openai/register.ts +4 -0
  258. package/src/proxy/formats/openai/schemas.ts +150 -0
  259. package/src/proxy/formats/openai/stream.ts +153 -0
  260. package/src/proxy/formats/protocol.ts +50 -0
  261. package/src/proxy/formats/providerRegistry.ts +51 -0
  262. package/src/proxy/formats/providers/index.ts +3 -0
  263. package/src/proxy/formats/registry.ts +61 -0
  264. package/src/proxy/handler.ts +389 -0
  265. package/src/proxy/logIndex.ts +187 -0
  266. package/src/proxy/logger.ts +99 -0
  267. package/src/proxy/providers.ts +234 -0
  268. package/src/proxy/schemas.ts +160 -0
  269. package/src/proxy/socketTracker.ts +158 -0
  270. package/src/proxy/store.ts +386 -0
  271. package/src/router.tsx +16 -0
  272. package/src/routes/__root.tsx +38 -0
  273. package/src/routes/api/config.paths.ts +14 -0
  274. package/src/routes/api/health.ts +11 -0
  275. package/src/routes/api/logs.$id.chunks.ts +36 -0
  276. package/src/routes/api/logs.$id.replay.ts +262 -0
  277. package/src/routes/api/logs.$id.ts +22 -0
  278. package/src/routes/api/logs.stream.ts +64 -0
  279. package/src/routes/api/logs.ts +30 -0
  280. package/src/routes/api/models.ts +10 -0
  281. package/src/routes/api/providers.$providerId.ts +45 -0
  282. package/src/routes/api/providers.ts +37 -0
  283. package/src/routes/api/sessions.ts +10 -0
  284. package/src/routes/index.tsx +6 -0
  285. package/src/routes/proxy/$.ts +15 -0
  286. package/styles/globals.css +121 -0
@@ -0,0 +1,2872 @@
1
+ import { c as createRouter, a as createRootRoute, b as createFileRoute, l as lazyRouteComponent, O as Outlet, H as HeadContent, S as Scripts } from "../_libs/tanstack__react-router.mjs";
2
+ import { j as jsxRuntimeExports } from "../_libs/react.mjs";
3
+ import { mkdirSync, writeFileSync, renameSync, copyFileSync, unlinkSync, existsSync, readFileSync } from "node:fs";
4
+ import path, { join, dirname, isAbsolute } from "node:path";
5
+ import { readFile, mkdir, writeFile, appendFile } from "node:fs/promises";
6
+ import { C as Conf } from "../_libs/conf.mjs";
7
+ import { randomUUID } from "crypto";
8
+ import { exec } from "node:child_process";
9
+ import { promisify } from "node:util";
10
+ import { o as object, _ as _enum, s as string, a as array, n as number, d as discriminatedUnion, l as literal, b as boolean, r as record, c as lazy, e as _null, u as union } from "../_libs/zod.mjs";
11
+ import "../_libs/tiny-warning.mjs";
12
+ import "../_libs/tanstack__router-core.mjs";
13
+ import "../_libs/cookie-es.mjs";
14
+ import "../_libs/tanstack__history.mjs";
15
+ import "../_libs/tiny-invariant.mjs";
16
+ import "../_libs/seroval.mjs";
17
+ import "../_libs/seroval-plugins.mjs";
18
+ import "node:stream/web";
19
+ import "node:stream";
20
+ import "../_libs/react-dom.mjs";
21
+ import "../_libs/isbot.mjs";
22
+ import "node:process";
23
+ import "node:crypto";
24
+ import "node:assert";
25
+ import "../_libs/dot-prop.mjs";
26
+ import "../_libs/env-paths.mjs";
27
+ import "node:os";
28
+ import "../_libs/atomically.mjs";
29
+ import "../_libs/stubborn-fs.mjs";
30
+ import "../_libs/stubborn-utils.mjs";
31
+ import "../_libs/when-exit.mjs";
32
+ import "../_libs/ajv.mjs";
33
+ import "../_libs/fast-deep-equal.mjs";
34
+ import "../_libs/json-schema-traverse.mjs";
35
+ import "../_libs/fast-uri.mjs";
36
+ import "../_libs/ajv-formats.mjs";
37
+ import "../_libs/debounce-fn.mjs";
38
+ import "../_libs/mimic-function.mjs";
39
+ import "../_libs/semver.mjs";
40
+ import "../_libs/uint8array-extras.mjs";
41
+ const appCss = "/assets/index-B3RwBPLW.css";
42
+ const Route$e = createRootRoute({
43
+ head: () => ({
44
+ meta: [
45
+ { charSet: "utf-8" },
46
+ { name: "viewport", content: "width=device-width, initial-scale=1" },
47
+ { title: "llm-inspector" }
48
+ ],
49
+ links: [{ rel: "stylesheet", href: appCss }]
50
+ }),
51
+ component: RootComponent
52
+ });
53
+ function RootComponent() {
54
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(RootDocument, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(Outlet, {}) });
55
+ }
56
+ function RootDocument({ children }) {
57
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("html", { lang: "en", className: "dark", children: [
58
+ /* @__PURE__ */ jsxRuntimeExports.jsx("head", { children: /* @__PURE__ */ jsxRuntimeExports.jsx(HeadContent, {}) }),
59
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("body", { children: [
60
+ children,
61
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Scripts, {})
62
+ ] })
63
+ ] });
64
+ }
65
+ const $$splitComponentImporter = () => import("./index-ByCLZu7J.mjs");
66
+ const Route$d = createFileRoute("/")({
67
+ component: lazyRouteComponent($$splitComponentImporter, "component")
68
+ });
69
+ const LOG_DIR_ENV = process.env["LOG_DIR"];
70
+ Number(process.env["LOG_RETENTION_DAYS"] ?? "7");
71
+ function getUserDataDir() {
72
+ if (process.platform === "win32") {
73
+ return process.env["APPDATA"] ?? path.join(process.env["USERPROFILE"] ?? "C:\\", ".llm-inspector");
74
+ }
75
+ return process.env["HOME"] ?? path.join("/tmp");
76
+ }
77
+ let resolvedLogDir = null;
78
+ function resolveLogDir() {
79
+ if (resolvedLogDir !== null) return resolvedLogDir;
80
+ const base = getUserDataDir();
81
+ if (LOG_DIR_ENV !== void 0) {
82
+ resolvedLogDir = path.isAbsolute(LOG_DIR_ENV) ? LOG_DIR_ENV : path.join(base, LOG_DIR_ENV);
83
+ } else {
84
+ resolvedLogDir = path.join(base, ".llm-inspector", "logs");
85
+ }
86
+ return resolvedLogDir;
87
+ }
88
+ function getLogFilePath() {
89
+ const date = /* @__PURE__ */ new Date();
90
+ const yyyy = date.getUTCFullYear();
91
+ const mm = String(date.getUTCMonth() + 1).padStart(2, "0");
92
+ const dd = String(date.getUTCDate()).padStart(2, "0");
93
+ return path.join(resolveLogDir(), `${yyyy}-${mm}-${dd}.jsonl`);
94
+ }
95
+ let writeBuffer = [];
96
+ let writeScheduled = false;
97
+ async function flushWriteBuffer() {
98
+ if (writeBuffer.length === 0) return;
99
+ const toWrite = writeBuffer.join("");
100
+ writeBuffer = [];
101
+ try {
102
+ const filePath = getLogFilePath();
103
+ await mkdir(path.dirname(filePath), { recursive: true });
104
+ await appendFile(filePath, toWrite, "utf-8");
105
+ } catch (err) {
106
+ console.error("[logger] Failed to write log entries:", err);
107
+ }
108
+ }
109
+ function scheduleFlush() {
110
+ if (writeScheduled) return;
111
+ writeScheduled = true;
112
+ void Promise.resolve().then(() => {
113
+ writeScheduled = false;
114
+ void flushWriteBuffer();
115
+ });
116
+ }
117
+ function appendLogEntry(entry) {
118
+ const line = JSON.stringify(entry) + "\n";
119
+ writeBuffer.push(line);
120
+ scheduleFlush();
121
+ }
122
+ const INDEX_VERSION = 1;
123
+ const INDEX_FILE = "logs.idx";
124
+ function getIndexPath() {
125
+ return join(resolveLogDir(), INDEX_FILE);
126
+ }
127
+ let cachedIndex = null;
128
+ function createEmptyIndex() {
129
+ return {
130
+ version: INDEX_VERSION,
131
+ entries: {},
132
+ maxId: 0
133
+ };
134
+ }
135
+ function isLogIndex(obj) {
136
+ if (typeof obj !== "object" || obj === null || Array.isArray(obj)) return false;
137
+ const versionDesc = Object.getOwnPropertyDescriptor(obj, "version");
138
+ const entriesDesc = Object.getOwnPropertyDescriptor(obj, "entries");
139
+ const maxIdDesc = Object.getOwnPropertyDescriptor(obj, "maxId");
140
+ return versionDesc !== void 0 && typeof versionDesc.value === "number" && entriesDesc !== void 0 && typeof entriesDesc.value === "object" && entriesDesc.value !== null && maxIdDesc !== void 0 && typeof maxIdDesc.value === "number";
141
+ }
142
+ async function loadIndex() {
143
+ if (cachedIndex !== null) return cachedIndex;
144
+ const indexPath = getIndexPath();
145
+ if (!existsSync(indexPath)) {
146
+ cachedIndex = createEmptyIndex();
147
+ return cachedIndex;
148
+ }
149
+ try {
150
+ const content = await readFile(indexPath, "utf-8");
151
+ const parsed = JSON.parse(content);
152
+ if (isLogIndex(parsed)) {
153
+ cachedIndex = parsed;
154
+ } else {
155
+ cachedIndex = createEmptyIndex();
156
+ }
157
+ return cachedIndex;
158
+ } catch (err) {
159
+ console.error("[logIndex] Failed to load index:", err);
160
+ cachedIndex = createEmptyIndex();
161
+ return cachedIndex;
162
+ }
163
+ }
164
+ async function saveIndex(index) {
165
+ const indexPath = getIndexPath();
166
+ const dir = dirname(indexPath);
167
+ try {
168
+ await mkdir(dir, { recursive: true });
169
+ } catch {
170
+ }
171
+ try {
172
+ await writeFile(indexPath, JSON.stringify(index), "utf-8");
173
+ } catch (err) {
174
+ console.error("[logIndex] Failed to save index:", err);
175
+ }
176
+ }
177
+ async function addToIndex(id, file, lineStart, lineEnd) {
178
+ const index = await loadIndex();
179
+ index.entries[id] = { id, file, lineStart, lineEnd };
180
+ if (id > index.maxId) {
181
+ index.maxId = id;
182
+ }
183
+ await saveIndex(index);
184
+ }
185
+ async function findInIndex(id) {
186
+ const index = await loadIndex();
187
+ return index.entries[id] ?? null;
188
+ }
189
+ async function getNextLogId() {
190
+ const index = await loadIndex();
191
+ return index.maxId + 1;
192
+ }
193
+ function getCurrentLogFile() {
194
+ const now = /* @__PURE__ */ new Date();
195
+ const yyyy = now.getUTCFullYear();
196
+ const mm = String(now.getUTCMonth() + 1).padStart(2, "0");
197
+ const dd = String(now.getUTCDate()).padStart(2, "0");
198
+ return `${yyyy}-${mm}-${dd}.jsonl`;
199
+ }
200
+ const JsonValueSchema = lazy(
201
+ () => union([
202
+ string(),
203
+ number(),
204
+ boolean(),
205
+ _null(),
206
+ array(JsonValueSchema),
207
+ record(string(), JsonValueSchema)
208
+ ])
209
+ );
210
+ const CacheControl = object({
211
+ type: string(),
212
+ ttl: string().optional(),
213
+ scope: string().optional()
214
+ });
215
+ const TextContentBlock = object({
216
+ type: literal("text"),
217
+ text: string(),
218
+ cache_control: CacheControl.optional()
219
+ });
220
+ const ThinkingContentBlock = object({
221
+ type: literal("thinking"),
222
+ thinking: string(),
223
+ signature: string().optional()
224
+ });
225
+ const ImageSourceBlock = object({
226
+ type: literal("base64"),
227
+ media_type: string(),
228
+ data: string()
229
+ });
230
+ const ImageContentBlock = object({
231
+ type: literal("image"),
232
+ source: ImageSourceBlock
233
+ });
234
+ const ToolUseContentBlock = object({
235
+ type: literal("tool_use"),
236
+ id: string(),
237
+ name: string(),
238
+ input: record(string(), JsonValueSchema)
239
+ });
240
+ const ToolResultContentItem = discriminatedUnion("type", [TextContentBlock, ImageContentBlock]);
241
+ const ToolResultContentBlock = object({
242
+ type: literal("tool_result"),
243
+ tool_use_id: string().optional(),
244
+ content: union([string(), array(ToolResultContentItem)]),
245
+ is_error: boolean().optional()
246
+ });
247
+ const ContentBlock = discriminatedUnion("type", [
248
+ TextContentBlock,
249
+ ThinkingContentBlock,
250
+ ImageContentBlock,
251
+ ToolUseContentBlock,
252
+ ToolResultContentBlock
253
+ ]);
254
+ const MessageContent = union([string(), array(ContentBlock)]);
255
+ const Message = object({
256
+ role: _enum(["user", "assistant"]),
257
+ content: MessageContent
258
+ });
259
+ const SystemBlock = object({
260
+ type: literal("text"),
261
+ text: string(),
262
+ cache_control: CacheControl.optional()
263
+ });
264
+ const InputSchema = object({
265
+ type: string(),
266
+ properties: record(string(), record(string(), JsonValueSchema)).optional(),
267
+ required: array(string()).optional(),
268
+ additionalProperties: boolean().optional(),
269
+ $schema: string().optional()
270
+ });
271
+ const ToolDefinition = object({
272
+ name: string(),
273
+ description: string().optional(),
274
+ input_schema: InputSchema.optional(),
275
+ cache_control: CacheControl.optional()
276
+ });
277
+ const ThinkingConfig = discriminatedUnion("type", [
278
+ object({ type: literal("enabled"), budget_tokens: number() }),
279
+ object({ type: literal("disabled") }),
280
+ object({ type: literal("adaptive") })
281
+ ]);
282
+ const AnthropicRequestSchema = object({
283
+ model: string(),
284
+ messages: array(Message),
285
+ system: array(SystemBlock).optional(),
286
+ tools: array(ToolDefinition).optional(),
287
+ max_tokens: number().optional(),
288
+ temperature: number().optional(),
289
+ stream: boolean().optional(),
290
+ thinking: ThinkingConfig.optional(),
291
+ metadata: object({
292
+ user_id: string().optional()
293
+ }).optional()
294
+ });
295
+ const ResponseContentBlock = discriminatedUnion("type", [
296
+ TextContentBlock,
297
+ ThinkingContentBlock,
298
+ ToolUseContentBlock
299
+ ]);
300
+ const ResponseUsageSchema = object({
301
+ input_tokens: number(),
302
+ output_tokens: number(),
303
+ cache_creation_input_tokens: number().optional(),
304
+ cache_read_input_tokens: number().optional()
305
+ });
306
+ const AnthropicResponseSchema$1 = object({
307
+ id: string(),
308
+ type: literal("message"),
309
+ model: string(),
310
+ role: literal("assistant"),
311
+ content: array(ResponseContentBlock),
312
+ stop_reason: string().nullable(),
313
+ stop_sequence: string().nullable(),
314
+ usage: ResponseUsageSchema
315
+ });
316
+ const SseMessageStartEvent = object({
317
+ type: literal("message_start"),
318
+ message: object({
319
+ id: string(),
320
+ type: literal("message"),
321
+ model: string(),
322
+ role: literal("assistant"),
323
+ content: array(ResponseContentBlock),
324
+ stop_reason: _null(),
325
+ stop_sequence: _null(),
326
+ usage: object({
327
+ input_tokens: number(),
328
+ cache_creation_input_tokens: number().optional(),
329
+ cache_read_input_tokens: number().optional()
330
+ }).passthrough()
331
+ })
332
+ });
333
+ const SseContentBlockStartEvent = object({
334
+ type: literal("content_block_start"),
335
+ index: number(),
336
+ content_block: ResponseContentBlock
337
+ });
338
+ const SseDeltaBlock = discriminatedUnion("type", [
339
+ object({ type: literal("text_delta"), text: string() }),
340
+ object({ type: literal("input_json_delta"), partial_json: string() }),
341
+ object({ type: literal("thinking_delta"), thinking: string() }),
342
+ object({ type: literal("signature_delta"), signature: string() })
343
+ ]);
344
+ const SseContentBlockDeltaEvent = object({
345
+ type: literal("content_block_delta"),
346
+ index: number(),
347
+ delta: SseDeltaBlock
348
+ });
349
+ const SseContentBlockStopEvent = object({
350
+ type: literal("content_block_stop"),
351
+ index: number()
352
+ });
353
+ const SseMessageDeltaEvent = object({
354
+ type: literal("message_delta"),
355
+ delta: object({
356
+ stop_reason: string().nullable(),
357
+ stop_sequence: string().nullable().optional()
358
+ }),
359
+ usage: object({
360
+ output_tokens: number(),
361
+ input_tokens: number().optional(),
362
+ cache_creation_input_tokens: number().optional(),
363
+ cache_read_input_tokens: number().optional()
364
+ }).passthrough()
365
+ });
366
+ const SseMessageStopEvent = object({
367
+ type: literal("message_stop")
368
+ });
369
+ const SsePingEvent = object({
370
+ type: literal("ping")
371
+ });
372
+ const SseEventSchema = discriminatedUnion("type", [
373
+ SseMessageStartEvent,
374
+ SseContentBlockStartEvent,
375
+ SseContentBlockDeltaEvent,
376
+ SseContentBlockStopEvent,
377
+ SseMessageDeltaEvent,
378
+ SseMessageStopEvent,
379
+ SsePingEvent
380
+ ]);
381
+ const InspectorRequestSchema = AnthropicRequestSchema;
382
+ const InspectorResponseSchema = AnthropicResponseSchema$1;
383
+ const OpenAIMessageContent = union([
384
+ string(),
385
+ array(
386
+ discriminatedUnion("type", [
387
+ object({ type: literal("text"), text: string() }),
388
+ object({
389
+ type: literal("image_url"),
390
+ image_url: object({ url: string(), detail: string().optional() })
391
+ })
392
+ ])
393
+ )
394
+ ]);
395
+ const OpenAIMessage = object({
396
+ role: _enum(["system", "user", "assistant", "tool"]),
397
+ content: OpenAIMessageContent,
398
+ name: string().optional(),
399
+ reasoning_content: string().optional()
400
+ });
401
+ const OpenAIFunctionCall = object({
402
+ name: string(),
403
+ arguments: string()
404
+ });
405
+ OpenAIMessage.extend({
406
+ content: union([string(), array(object({ type: literal("text"), text: string() }))]).optional(),
407
+ function_call: OpenAIFunctionCall.optional()
408
+ });
409
+ const OpenAIToolDefinition = object({
410
+ type: literal("function"),
411
+ function: object({
412
+ name: string(),
413
+ description: string().optional(),
414
+ parameters: record(string(), JsonValueSchema)
415
+ })
416
+ });
417
+ const OpenAIRequestSchema = object({
418
+ model: string(),
419
+ messages: array(OpenAIMessage),
420
+ temperature: number().optional(),
421
+ max_tokens: number().optional(),
422
+ stream: boolean().optional(),
423
+ tools: array(OpenAIToolDefinition).optional(),
424
+ tool_choice: union([
425
+ object({ type: literal("auto") }),
426
+ object({ type: literal("none") }),
427
+ object({ type: literal("function"), function: object({ name: string() }) })
428
+ ]).optional(),
429
+ user: string().optional()
430
+ });
431
+ const OpenAIChoiceDelta = object({
432
+ role: _enum(["assistant"]).optional(),
433
+ content: string().nullable().optional(),
434
+ reasoning_content: string().nullable().optional(),
435
+ function_call: object({ name: string().optional(), arguments: string().optional() }).optional(),
436
+ tool_calls: array(
437
+ object({
438
+ index: number(),
439
+ id: string().optional(),
440
+ type: literal("function").optional(),
441
+ function: object({
442
+ name: string().optional(),
443
+ arguments: string().optional()
444
+ })
445
+ })
446
+ ).optional()
447
+ });
448
+ const OpenAIChoice = object({
449
+ index: number(),
450
+ message: object({
451
+ role: _enum(["assistant"]),
452
+ content: string().nullable(),
453
+ reasoning_content: string().optional(),
454
+ function_call: object({ name: string(), arguments: string() }).optional()
455
+ }).optional(),
456
+ delta: OpenAIChoiceDelta.optional(),
457
+ finish_reason: string().nullable()
458
+ });
459
+ const OpenAIResponseSchema$1 = object({
460
+ id: string(),
461
+ object: literal("chat.completion"),
462
+ created: number(),
463
+ model: string(),
464
+ choices: array(OpenAIChoice),
465
+ usage: object({
466
+ prompt_tokens: number(),
467
+ completion_tokens: number(),
468
+ total_tokens: number()
469
+ })
470
+ });
471
+ const OpenAISSERawChunkSchema = object({
472
+ id: string(),
473
+ object: literal("chat.completion.chunk"),
474
+ created: number(),
475
+ model: string(),
476
+ choices: array(
477
+ object({
478
+ index: number(),
479
+ delta: OpenAIChoiceDelta,
480
+ finish_reason: string().nullable().optional()
481
+ })
482
+ ),
483
+ usage: object({
484
+ prompt_tokens: number(),
485
+ completion_tokens: number(),
486
+ total_tokens: number()
487
+ }).nullable().optional()
488
+ });
489
+ function parseOpenAIResponse(rawBody) {
490
+ try {
491
+ const json = JSON.parse(rawBody);
492
+ const result = OpenAIResponseSchema$1.safeParse(json);
493
+ if (result.success) return result.data;
494
+ return null;
495
+ } catch {
496
+ return null;
497
+ }
498
+ }
499
+ const StreamingChunkSchema$1 = object({
500
+ index: number(),
501
+ timestamp: number(),
502
+ type: string(),
503
+ data: JsonValueSchema
504
+ });
505
+ const StreamingChunksArraySchema = object({
506
+ chunks: array(StreamingChunkSchema$1),
507
+ truncated: boolean().optional().default(false)
508
+ });
509
+ const CapturedLogSchema = object({
510
+ id: number(),
511
+ timestamp: string(),
512
+ method: string(),
513
+ path: string(),
514
+ model: string().nullable(),
515
+ sessionId: string().nullable(),
516
+ rawRequestBody: string().nullable(),
517
+ responseStatus: number().nullable(),
518
+ responseText: string().nullable(),
519
+ inputTokens: number().nullable(),
520
+ outputTokens: number().nullable(),
521
+ cacheCreationInputTokens: number().nullable(),
522
+ cacheReadInputTokens: number().nullable(),
523
+ elapsedMs: number().nullable(),
524
+ streaming: boolean(),
525
+ userAgent: string().nullable(),
526
+ origin: string().nullable(),
527
+ rawHeaders: record(string(), string()).optional(),
528
+ /** Headers sent to the upstream LLM */
529
+ headers: record(string(), string()).optional(),
530
+ apiFormat: _enum(["anthropic", "openai", "unknown"]).default("unknown"),
531
+ isTest: boolean().optional().default(false),
532
+ providerName: string().nullable().optional(),
533
+ clientPort: number().nullable().optional(),
534
+ clientPid: number().nullable().optional(),
535
+ clientCwd: string().nullable().optional(),
536
+ clientProjectFolder: string().nullable().optional(),
537
+ streamingChunks: StreamingChunksArraySchema.optional(),
538
+ streamingChunksPath: string().nullable().optional()
539
+ });
540
+ const RequestModelSchema = object({
541
+ model: string()
542
+ });
543
+ function extractModelFromBody(body) {
544
+ try {
545
+ const json = JSON.parse(body);
546
+ const parsed = RequestModelSchema.safeParse(json);
547
+ if (parsed.success) {
548
+ return parsed.data.model;
549
+ }
550
+ return null;
551
+ } catch {
552
+ return null;
553
+ }
554
+ }
555
+ function parseRequest(rawBody) {
556
+ if (rawBody === null) return null;
557
+ try {
558
+ const json = JSON.parse(rawBody);
559
+ const result = InspectorRequestSchema.safeParse(json);
560
+ if (result.success) return result.data;
561
+ return null;
562
+ } catch {
563
+ return null;
564
+ }
565
+ }
566
+ const StreamingChunkSchema = object({
567
+ index: number(),
568
+ timestamp: number(),
569
+ type: string(),
570
+ data: JsonValueSchema
571
+ });
572
+ const StreamingChunksDataSchema = object({
573
+ chunks: array(StreamingChunkSchema),
574
+ truncated: boolean().optional()
575
+ });
576
+ const CHUNKS_DIR_ENV = process.env["CHUNKS_DIR"];
577
+ function getChunksDir() {
578
+ const isWindows = process.platform === "win32";
579
+ const base = isWindows ? process.env["APPDATA"] ?? join(process.env["USERPROFILE"] ?? "C:\\", ".llm-inspector") : process.env["HOME"] ?? "/tmp";
580
+ if (CHUNKS_DIR_ENV !== void 0) {
581
+ return isAbsolute(CHUNKS_DIR_ENV) ? CHUNKS_DIR_ENV : join(base, CHUNKS_DIR_ENV);
582
+ }
583
+ return join(base, ".llm-inspector", "chunks");
584
+ }
585
+ function getChunkFilePath(logId) {
586
+ return join(getChunksDir(), `${logId}.json`);
587
+ }
588
+ function getTempFilePath(logId) {
589
+ return join(getChunksDir(), `.${logId}.tmp`);
590
+ }
591
+ function writeChunks(logId, chunks, truncated) {
592
+ const dir = getChunksDir();
593
+ const targetPath = getChunkFilePath(logId);
594
+ const tempPath = getTempFilePath(logId);
595
+ try {
596
+ mkdirSync(dir, { recursive: true });
597
+ } catch (err) {
598
+ console.error("[chunkStorage] Failed to create chunks directory:", err);
599
+ }
600
+ const data = { chunks, truncated };
601
+ try {
602
+ writeFileSync(tempPath, JSON.stringify(data), "utf-8");
603
+ } catch (err) {
604
+ console.error("[chunkStorage] Failed to write chunks temp file:", err);
605
+ return targetPath;
606
+ }
607
+ try {
608
+ renameSync(tempPath, targetPath);
609
+ } catch (err) {
610
+ console.warn("[chunkStorage] Rename failed, falling back to copy+delete:", err);
611
+ try {
612
+ copyFileSync(tempPath, targetPath);
613
+ unlinkSync(tempPath);
614
+ } catch (copyErr) {
615
+ console.error("[chunkStorage] Failed to copy chunks file:", copyErr);
616
+ }
617
+ }
618
+ return targetPath;
619
+ }
620
+ function readChunks(path2) {
621
+ if (!existsSync(path2)) {
622
+ return null;
623
+ }
624
+ try {
625
+ const content = readFileSync(path2, "utf-8");
626
+ const parsed = JSON.parse(content);
627
+ const result = StreamingChunksDataSchema.safeParse(parsed);
628
+ if (!result.success) return null;
629
+ return result.data;
630
+ } catch {
631
+ return null;
632
+ }
633
+ }
634
+ const LooseRequestSchema = object({
635
+ model: string().optional(),
636
+ metadata: object({ user_id: string().optional() }).passthrough().optional()
637
+ });
638
+ const MAX_MEMORY_CACHE = 100;
639
+ const memoryCache = /* @__PURE__ */ new Map();
640
+ let headId = null;
641
+ let tailId = null;
642
+ const prevMap = /* @__PURE__ */ new Map();
643
+ const nextMap = /* @__PURE__ */ new Map();
644
+ function evictOldestIfNeeded() {
645
+ while (memoryCache.size >= MAX_MEMORY_CACHE && headId !== null) {
646
+ const oldest = headId;
647
+ const next = nextMap.get(oldest) ?? null;
648
+ memoryCache.delete(oldest);
649
+ prevMap.delete(oldest);
650
+ nextMap.delete(oldest);
651
+ if (next !== null) {
652
+ prevMap.set(next, null);
653
+ headId = next;
654
+ } else {
655
+ headId = null;
656
+ tailId = null;
657
+ }
658
+ }
659
+ }
660
+ function addToCache(log) {
661
+ evictOldestIfNeeded();
662
+ if (tailId !== null) {
663
+ nextMap.set(tailId, log.id);
664
+ prevMap.set(log.id, tailId);
665
+ } else {
666
+ headId = log.id;
667
+ prevMap.set(log.id, null);
668
+ }
669
+ nextMap.set(log.id, null);
670
+ tailId = log.id;
671
+ memoryCache.set(log.id, log);
672
+ }
673
+ async function addTestLogEntry(entry) {
674
+ const id = await getNextLogId();
675
+ const index = await loadIndex();
676
+ if (id > index.maxId) {
677
+ index.maxId = id;
678
+ await saveIndex(index);
679
+ }
680
+ let streamingChunksPath = null;
681
+ if (entry.streamingChunks !== void 0 && entry.streamingChunks.chunks.length > 0) {
682
+ streamingChunksPath = writeChunks(
683
+ id,
684
+ entry.streamingChunks.chunks,
685
+ entry.streamingChunks.truncated
686
+ );
687
+ }
688
+ const log = {
689
+ id,
690
+ ...entry,
691
+ streamingChunksPath
692
+ };
693
+ addToCache(log);
694
+ emitLogUpdate(log);
695
+ return log;
696
+ }
697
+ async function createLog(method, path2, requestBody, headers, clientInfo, rawHeaders, upstreamHeaders, apiFormat = "unknown") {
698
+ let model = null;
699
+ let sessionId = null;
700
+ if (requestBody !== null) {
701
+ try {
702
+ const json = JSON.parse(requestBody);
703
+ const loose = LooseRequestSchema.safeParse(json);
704
+ if (loose.success) {
705
+ model = loose.data.model ?? null;
706
+ sessionId = loose.data.metadata?.user_id ?? headers.get("x-session-affinity") ?? null;
707
+ }
708
+ } catch {
709
+ }
710
+ }
711
+ const userAgent = headers.get("user-agent");
712
+ const origin = headers.get("origin");
713
+ const id = await getNextLogId();
714
+ const log = {
715
+ id,
716
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
717
+ method,
718
+ path: path2,
719
+ model,
720
+ sessionId,
721
+ rawRequestBody: requestBody,
722
+ responseStatus: null,
723
+ responseText: null,
724
+ inputTokens: null,
725
+ outputTokens: null,
726
+ cacheCreationInputTokens: null,
727
+ cacheReadInputTokens: null,
728
+ elapsedMs: null,
729
+ streaming: false,
730
+ userAgent,
731
+ origin,
732
+ rawHeaders,
733
+ headers: upstreamHeaders,
734
+ apiFormat,
735
+ isTest: false,
736
+ providerName: null,
737
+ clientPort: clientInfo?.port ?? null,
738
+ clientPid: clientInfo?.pid ?? null,
739
+ clientCwd: clientInfo?.cwd ?? null,
740
+ clientProjectFolder: clientInfo?.projectFolder ?? null,
741
+ streamingChunksPath: null
742
+ };
743
+ const logFile = getCurrentLogFile();
744
+ appendLogEntry(log);
745
+ await addToIndex(id, logFile, -1, -1);
746
+ addToCache(log);
747
+ emitLogUpdate(log);
748
+ return log;
749
+ }
750
+ async function getLogById(id) {
751
+ const cached = memoryCache.get(id);
752
+ if (cached !== void 0) {
753
+ return cached;
754
+ }
755
+ const entry = await findInIndex(id);
756
+ if (entry === null) {
757
+ return null;
758
+ }
759
+ try {
760
+ const filePath = join(resolveLogDir(), entry.file);
761
+ if (!existsSync(filePath)) {
762
+ return null;
763
+ }
764
+ const content = readFileSync(filePath, "utf-8");
765
+ const lines = content.split("\n");
766
+ let lastMatch = null;
767
+ for (const line of lines) {
768
+ if (line.trim() === "") continue;
769
+ try {
770
+ const parsed = JSON.parse(line);
771
+ if (typeof parsed === "object" && parsed !== null) {
772
+ const desc = Object.getOwnPropertyDescriptor(parsed, "id");
773
+ if (desc !== void 0 && typeof desc.value === "number" && desc.value === id) {
774
+ const result = CapturedLogSchema.safeParse(parsed);
775
+ if (result.success) {
776
+ lastMatch = result.data;
777
+ if (result.data.responseStatus !== null) {
778
+ return result.data;
779
+ }
780
+ }
781
+ }
782
+ }
783
+ } catch {
784
+ }
785
+ }
786
+ if (lastMatch !== null) return lastMatch;
787
+ } catch (err) {
788
+ console.error("[store] Failed to read log from disk:", err);
789
+ }
790
+ return null;
791
+ }
792
+ function getFilteredLogs(sessionId, model) {
793
+ const cachedLogs = Array.from(memoryCache.values()).sort((a, b) => a.id - b.id);
794
+ return cachedLogs.filter((l) => {
795
+ if (sessionId !== void 0 && l.sessionId !== sessionId) return false;
796
+ if (model !== void 0 && l.model !== model) return false;
797
+ return true;
798
+ });
799
+ }
800
+ function getSessions() {
801
+ const set = /* @__PURE__ */ new Set();
802
+ for (const l of memoryCache.values()) {
803
+ if (l.sessionId !== null && l.sessionId !== "") set.add(l.sessionId);
804
+ }
805
+ return [...set];
806
+ }
807
+ function getModels() {
808
+ const set = /* @__PURE__ */ new Set();
809
+ for (const l of memoryCache.values()) {
810
+ if (l.model !== null && l.model !== "") set.add(l.model);
811
+ }
812
+ return [...set];
813
+ }
814
+ function clearAllLogs() {
815
+ const count = memoryCache.size;
816
+ memoryCache.clear();
817
+ headId = null;
818
+ tailId = null;
819
+ prevMap.clear();
820
+ nextMap.clear();
821
+ return { cleared: count };
822
+ }
823
+ const sseHandlers = /* @__PURE__ */ new Set();
824
+ function onLogUpdate(handler) {
825
+ sseHandlers.add(handler);
826
+ return () => {
827
+ sseHandlers.delete(handler);
828
+ };
829
+ }
830
+ function emitLogUpdate(log) {
831
+ for (const handler of sseHandlers) {
832
+ try {
833
+ handler(log);
834
+ } catch {
835
+ sseHandlers.delete(handler);
836
+ }
837
+ }
838
+ }
839
+ const DEFAULT_UPSTREAM$1 = "https://api.anthropic.com";
840
+ const DEFAULT_OPENAI_UPSTREAM$1 = "https://api.openai.com/v1";
841
+ const PROXY_IDENTITY = process.env["PROXY_IDENTITY"] ?? "inspector9527@Tony";
842
+ const PATH_V1_CHAT_COMPLETIONS = "/v1/chat/completions";
843
+ const PATH_CHAT_COMPLETIONS = "/chat/completions";
844
+ const PATH_V1_MESSAGES = "/v1/messages";
845
+ const HEADER_CONTENT_TYPE = "content-type";
846
+ const HEADER_USER_AGENT = "user-agent";
847
+ const HEADER_X_PROXY_IDENTITY = "x-proxy-identity";
848
+ const HEADER_AUTHORIZATION = "authorization";
849
+ const HEADER_X_API_KEY = "x-api-key";
850
+ const HEADER_CONTENT_ENCODING = "content-encoding";
851
+ const HEADER_CONTENT_LENGTH = "content-length";
852
+ const HEADER_HOST = "host";
853
+ const AUTH_HEADER_X_API_KEY = "x-api-key";
854
+ const CONTENT_TYPE_EVENT_STREAM = "text/event-stream";
855
+ const STATUS_FORBIDDEN = 403;
856
+ const STATUS_BAD_GATEWAY = 502;
857
+ const PRESERVE_HEADERS = /* @__PURE__ */ new Set([HEADER_CONTENT_TYPE]);
858
+ class FormatRegistryImpl {
859
+ handlers = /* @__PURE__ */ new Map();
860
+ pathMap = /* @__PURE__ */ new Map();
861
+ register(handler) {
862
+ this.handlers.set(handler.format, handler);
863
+ }
864
+ registerPath(path2, format) {
865
+ this.pathMap.set(path2, format);
866
+ }
867
+ /** Get handler by format identifier */
868
+ get(format) {
869
+ return this.handlers.get(format);
870
+ }
871
+ /** Get handler matching a request path */
872
+ getByPath(path2) {
873
+ const messagesPath = path2.split("?")[0] ?? "";
874
+ if (messagesPath === PATH_V1_MESSAGES) return this.handlers.get("anthropic");
875
+ if (messagesPath === PATH_V1_CHAT_COMPLETIONS || messagesPath === PATH_CHAT_COMPLETIONS) {
876
+ return this.handlers.get("openai");
877
+ }
878
+ return void 0;
879
+ }
880
+ /** Detect format from request body content */
881
+ detectFormat(rawBody) {
882
+ if (rawBody === null) return "unknown";
883
+ for (const handler of this.handlers.values()) {
884
+ if (handler.detectFormat(rawBody)) return handler.format;
885
+ }
886
+ return "unknown";
887
+ }
888
+ }
889
+ const formatRegistry = new FormatRegistryImpl();
890
+ function formatForPath(path2) {
891
+ return formatRegistry.getByPath(path2) ?? null;
892
+ }
893
+ formatRegistry.registerPath(PATH_V1_MESSAGES, "anthropic");
894
+ formatRegistry.registerPath(PATH_V1_CHAT_COMPLETIONS, "openai");
895
+ formatRegistry.registerPath(PATH_CHAT_COMPLETIONS, "openai");
896
+ function parseInputJson(json) {
897
+ if (json === "") return {};
898
+ try {
899
+ const parsed = JSON.parse(json);
900
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
901
+ return { ...parsed };
902
+ }
903
+ return {};
904
+ } catch {
905
+ return {};
906
+ }
907
+ }
908
+ function finalizeBlock(block) {
909
+ switch (block.type) {
910
+ case "text":
911
+ return { type: "text", text: block.text };
912
+ case "thinking": {
913
+ const out = { type: "thinking", thinking: block.thinking };
914
+ if (block.signature !== "") out.signature = block.signature;
915
+ return out;
916
+ }
917
+ case "tool_use":
918
+ return {
919
+ type: "tool_use",
920
+ id: block.id,
921
+ name: block.name,
922
+ input: parseInputJson(block.inputJson)
923
+ };
924
+ }
925
+ }
926
+ function extractAnthropicStream(raw, log, fallbackModel, collectChunks) {
927
+ const blocks = /* @__PURE__ */ new Map();
928
+ let id = "";
929
+ let model = "";
930
+ let stopReason = null;
931
+ let stopSequence = null;
932
+ let inputTokens = 0;
933
+ let outputTokens = 0;
934
+ const MAX_CHUNKS = 1e3;
935
+ let chunkIndex = 0;
936
+ let streamStartMs = 0;
937
+ const chunks = [];
938
+ for (const line of raw.split("\n")) {
939
+ if (!line.startsWith("data: ")) continue;
940
+ try {
941
+ const json = JSON.parse(line.slice(6));
942
+ const parsed = SseEventSchema.safeParse(json);
943
+ if (!parsed.success) continue;
944
+ const data = parsed.data;
945
+ if (chunkIndex === 0) streamStartMs = Date.now();
946
+ if (collectChunks === true && chunks.length < MAX_CHUNKS) {
947
+ chunks.push({
948
+ index: chunkIndex,
949
+ timestamp: Date.now() - streamStartMs,
950
+ type: data.type,
951
+ data: JsonValueSchema.parse(data)
952
+ });
953
+ }
954
+ chunkIndex++;
955
+ switch (data.type) {
956
+ case "message_start":
957
+ id = data.message.id;
958
+ model = data.message.model;
959
+ inputTokens = data.message.usage.input_tokens;
960
+ log.inputTokens = inputTokens;
961
+ log.cacheCreationInputTokens = data.message.usage.cache_creation_input_tokens ?? null;
962
+ log.cacheReadInputTokens = data.message.usage.cache_read_input_tokens ?? null;
963
+ if (log.model === null) log.model = model;
964
+ break;
965
+ case "content_block_start": {
966
+ const cb = data.content_block;
967
+ if (cb.type === "text") {
968
+ blocks.set(data.index, { type: "text", text: cb.text ?? "" });
969
+ } else if (cb.type === "thinking") {
970
+ blocks.set(data.index, {
971
+ type: "thinking",
972
+ thinking: cb.thinking ?? "",
973
+ signature: cb.signature ?? ""
974
+ });
975
+ } else {
976
+ blocks.set(data.index, {
977
+ type: "tool_use",
978
+ id: cb.id,
979
+ name: cb.name,
980
+ inputJson: ""
981
+ });
982
+ }
983
+ break;
984
+ }
985
+ case "content_block_delta": {
986
+ const block = blocks.get(data.index);
987
+ if (block === void 0) break;
988
+ const delta = data.delta;
989
+ if (delta.type === "text_delta" && block.type === "text") {
990
+ block.text += delta.text;
991
+ } else if (delta.type === "thinking_delta" && block.type === "thinking") {
992
+ block.thinking += delta.thinking;
993
+ } else if (delta.type === "signature_delta" && block.type === "thinking") {
994
+ block.signature += delta.signature;
995
+ } else if (delta.type === "input_json_delta" && block.type === "tool_use") {
996
+ block.inputJson += delta.partial_json;
997
+ }
998
+ break;
999
+ }
1000
+ case "message_delta":
1001
+ stopReason = data.delta.stop_reason;
1002
+ stopSequence = data.delta.stop_sequence ?? null;
1003
+ outputTokens = data.usage.output_tokens;
1004
+ log.outputTokens = outputTokens;
1005
+ break;
1006
+ case "content_block_stop":
1007
+ case "message_stop":
1008
+ case "ping":
1009
+ break;
1010
+ }
1011
+ } catch {
1012
+ }
1013
+ }
1014
+ if (collectChunks === true) {
1015
+ log.streamingChunks = {
1016
+ chunks,
1017
+ truncated: chunkIndex > MAX_CHUNKS
1018
+ };
1019
+ }
1020
+ const orderedContent = [...blocks.entries()].sort(([a], [b]) => a - b).map(([, block]) => finalizeBlock(block));
1021
+ return JSON.stringify({
1022
+ id,
1023
+ type: "message",
1024
+ model: model !== "" ? model : fallbackModel ?? "",
1025
+ role: "assistant",
1026
+ content: orderedContent,
1027
+ stop_reason: stopReason,
1028
+ stop_sequence: stopSequence,
1029
+ usage: { input_tokens: inputTokens, output_tokens: outputTokens }
1030
+ });
1031
+ }
1032
+ const AnthropicFormatHandler = {
1033
+ format: "anthropic",
1034
+ parseRequest(rawBody, headers) {
1035
+ try {
1036
+ const json = JSON.parse(rawBody);
1037
+ const result = AnthropicRequestSchema.safeParse(json);
1038
+ if (result.success) {
1039
+ return {
1040
+ model: result.data.model,
1041
+ sessionId: result.data.metadata?.user_id ?? headers?.get("x-session-affinity") ?? null
1042
+ };
1043
+ }
1044
+ return null;
1045
+ } catch {
1046
+ return null;
1047
+ }
1048
+ },
1049
+ extractTokens(responseBody) {
1050
+ try {
1051
+ const json = JSON.parse(responseBody);
1052
+ const result = AnthropicResponseSchema$1.safeParse(json);
1053
+ if (result.success) {
1054
+ return {
1055
+ inputTokens: result.data.usage.input_tokens,
1056
+ outputTokens: result.data.usage.output_tokens,
1057
+ cacheCreationInputTokens: result.data.usage.cache_creation_input_tokens ?? null,
1058
+ cacheReadInputTokens: result.data.usage.cache_read_input_tokens ?? null
1059
+ };
1060
+ }
1061
+ } catch {
1062
+ }
1063
+ return {
1064
+ inputTokens: null,
1065
+ outputTokens: null,
1066
+ cacheCreationInputTokens: null,
1067
+ cacheReadInputTokens: null
1068
+ };
1069
+ },
1070
+ extractStream(raw, log, fallbackModel, collectChunks) {
1071
+ return extractAnthropicStream(raw, log, fallbackModel, collectChunks);
1072
+ },
1073
+ detectFormat(rawBody) {
1074
+ if (rawBody === null) return false;
1075
+ try {
1076
+ const json = JSON.parse(rawBody);
1077
+ if (typeof json === "object" && json !== null && !Array.isArray(json)) {
1078
+ const keys = Object.keys(json);
1079
+ if (keys.includes("model") && keys.includes("messages")) {
1080
+ if (keys.includes("system") || keys.includes("tools")) {
1081
+ return true;
1082
+ }
1083
+ }
1084
+ }
1085
+ return false;
1086
+ } catch {
1087
+ return false;
1088
+ }
1089
+ }
1090
+ };
1091
+ formatRegistry.register(AnthropicFormatHandler);
1092
+ class ProviderRegistryImpl {
1093
+ providers = [];
1094
+ /**
1095
+ * Register a provider with the registry.
1096
+ * @param provider - Provider implementation to register
1097
+ */
1098
+ register(provider) {
1099
+ this.providers.push(provider);
1100
+ }
1101
+ /**
1102
+ * Find the first provider that matches the given model.
1103
+ * @param model - Model name to match
1104
+ * @returns Matching provider or null if none found
1105
+ */
1106
+ findProvider(model) {
1107
+ let fallback = null;
1108
+ for (const provider of this.providers) {
1109
+ if (provider.matches(model)) {
1110
+ if (provider.name !== "anthropic") {
1111
+ return provider;
1112
+ }
1113
+ fallback = provider;
1114
+ }
1115
+ }
1116
+ return fallback;
1117
+ }
1118
+ /**
1119
+ * Get all registered providers.
1120
+ * @returns Array of all registered providers
1121
+ */
1122
+ getAll() {
1123
+ return [...this.providers];
1124
+ }
1125
+ }
1126
+ const registry = new ProviderRegistryImpl();
1127
+ const DEFAULT_UPSTREAM = "https://api.anthropic.com";
1128
+ const DEFAULT_ROUTES = {
1129
+ MiniMax: "https://api.minimaxi.com/anthropic"
1130
+ };
1131
+ const anthropicProvider = {
1132
+ name: "anthropic",
1133
+ matches(model) {
1134
+ const modelLower = model.toLowerCase();
1135
+ if (modelLower.startsWith("openai-")) {
1136
+ return false;
1137
+ }
1138
+ if (modelLower.includes("claude") || modelLower.includes("anthropic")) {
1139
+ return true;
1140
+ }
1141
+ for (const prefix of Object.keys(DEFAULT_ROUTES)) {
1142
+ if (modelLower.startsWith(prefix.toLowerCase())) {
1143
+ return true;
1144
+ }
1145
+ }
1146
+ return true;
1147
+ },
1148
+ getUpstreamBase(_isChatCompletions, providerConfig) {
1149
+ if (providerConfig?.baseUrl !== void 0) {
1150
+ return providerConfig.baseUrl;
1151
+ }
1152
+ return DEFAULT_UPSTREAM;
1153
+ },
1154
+ extractStream(raw, log, fallbackModel, collectChunks) {
1155
+ return extractAnthropicStream(raw, log, fallbackModel, collectChunks);
1156
+ }
1157
+ };
1158
+ registry.register(anthropicProvider);
1159
+ function extractOpenAIStream(raw, log, fallbackModel, collectChunks = true) {
1160
+ let id = "";
1161
+ let model = "";
1162
+ let completionContent = "";
1163
+ let reasoningContent = "";
1164
+ let finishReason = null;
1165
+ let promptTokens = 0;
1166
+ let completionTokens = 0;
1167
+ let usageCaptured = false;
1168
+ let started = false;
1169
+ const toolCalls = [];
1170
+ const MAX_CHUNKS = 1e3;
1171
+ let chunkIndex = 0;
1172
+ let streamStartMs = 0;
1173
+ const chunks = [];
1174
+ for (const line of raw.split("\n")) {
1175
+ if (!line.startsWith("data: ")) continue;
1176
+ const dataStr = line.slice(6).trim();
1177
+ if (dataStr === "[DONE]") break;
1178
+ try {
1179
+ const parsed = JSON.parse(dataStr);
1180
+ const chunkResult = OpenAISSERawChunkSchema.safeParse(parsed);
1181
+ if (!chunkResult.success) continue;
1182
+ const chunk = chunkResult.data;
1183
+ if (chunkIndex === 0) streamStartMs = Date.now();
1184
+ if (collectChunks === true && chunks.length < MAX_CHUNKS) {
1185
+ const jsonResult = JsonValueSchema.safeParse(chunk);
1186
+ if (jsonResult.success) {
1187
+ chunks.push({
1188
+ index: chunkIndex,
1189
+ timestamp: Date.now() - streamStartMs,
1190
+ type: "chat.completion.chunk",
1191
+ data: jsonResult.data
1192
+ });
1193
+ }
1194
+ }
1195
+ chunkIndex++;
1196
+ if (!started) {
1197
+ id = chunk.id;
1198
+ model = chunk.model;
1199
+ started = true;
1200
+ }
1201
+ if (!usageCaptured && chunk.usage !== void 0 && chunk.usage !== null) {
1202
+ promptTokens = chunk.usage.prompt_tokens;
1203
+ completionTokens = chunk.usage.completion_tokens;
1204
+ log.inputTokens = promptTokens;
1205
+ usageCaptured = true;
1206
+ }
1207
+ for (const choice of chunk.choices) {
1208
+ const delta = choice.delta;
1209
+ if (delta.content !== void 0 && delta.content !== null) {
1210
+ completionContent += delta.content;
1211
+ }
1212
+ if (delta.reasoning_content !== void 0 && delta.reasoning_content !== null) {
1213
+ reasoningContent += delta.reasoning_content;
1214
+ }
1215
+ if (choice.finish_reason !== void 0 && choice.finish_reason !== null) {
1216
+ finishReason = choice.finish_reason;
1217
+ }
1218
+ if (delta.tool_calls !== void 0 && delta.tool_calls !== null) {
1219
+ for (const tc of delta.tool_calls) {
1220
+ let existing = toolCalls.find((t) => t.index === tc.index);
1221
+ if (!existing) {
1222
+ existing = {
1223
+ index: tc.index,
1224
+ type: "function",
1225
+ function: { name: "", arguments: "" }
1226
+ };
1227
+ toolCalls.push(existing);
1228
+ }
1229
+ if (tc.id !== void 0) existing.id = tc.id;
1230
+ if (tc.function?.name !== void 0) existing.function.name += tc.function.name;
1231
+ if (tc.function?.arguments !== void 0)
1232
+ existing.function.arguments += tc.function.arguments;
1233
+ }
1234
+ }
1235
+ }
1236
+ } catch {
1237
+ }
1238
+ }
1239
+ if (collectChunks === true) {
1240
+ log.streamingChunks = {
1241
+ chunks,
1242
+ truncated: chunkIndex > MAX_CHUNKS
1243
+ };
1244
+ }
1245
+ if (!usageCaptured) {
1246
+ completionTokens = Math.ceil(completionContent.length / 4);
1247
+ }
1248
+ log.outputTokens = completionTokens;
1249
+ const message = {
1250
+ role: "assistant",
1251
+ content: completionContent
1252
+ };
1253
+ if (reasoningContent) message.reasoning_content = reasoningContent;
1254
+ if (toolCalls.length > 0) message.tool_calls = toolCalls;
1255
+ return JSON.stringify({
1256
+ id,
1257
+ object: "chat.completion",
1258
+ created: Math.floor(Date.now() / 1e3),
1259
+ model: model !== "" ? model : fallbackModel ?? "",
1260
+ choices: [
1261
+ {
1262
+ index: 0,
1263
+ message,
1264
+ finish_reason: finishReason
1265
+ }
1266
+ ],
1267
+ usage: {
1268
+ prompt_tokens: promptTokens,
1269
+ completion_tokens: completionTokens,
1270
+ total_tokens: promptTokens + completionTokens
1271
+ }
1272
+ });
1273
+ }
1274
+ const OpenAIFormatHandler = {
1275
+ format: "openai",
1276
+ parseRequest(rawBody, headers) {
1277
+ try {
1278
+ const json = JSON.parse(rawBody);
1279
+ const result = OpenAIRequestSchema.safeParse(json);
1280
+ if (result.success) {
1281
+ return {
1282
+ model: result.data.model,
1283
+ sessionId: headers?.get("x-session-affinity") ?? null
1284
+ };
1285
+ }
1286
+ return null;
1287
+ } catch {
1288
+ return null;
1289
+ }
1290
+ },
1291
+ extractTokens(responseBody) {
1292
+ const parsed = parseOpenAIResponse(responseBody);
1293
+ if (parsed) {
1294
+ return {
1295
+ inputTokens: parsed.usage.prompt_tokens,
1296
+ outputTokens: parsed.usage.completion_tokens,
1297
+ cacheCreationInputTokens: null,
1298
+ cacheReadInputTokens: null
1299
+ };
1300
+ }
1301
+ return {
1302
+ inputTokens: null,
1303
+ outputTokens: null,
1304
+ cacheCreationInputTokens: null,
1305
+ cacheReadInputTokens: null
1306
+ };
1307
+ },
1308
+ extractStream(raw, log, fallbackModel, collectChunks) {
1309
+ return extractOpenAIStream(raw, log, fallbackModel, collectChunks);
1310
+ },
1311
+ detectFormat(rawBody) {
1312
+ if (rawBody === null) return false;
1313
+ try {
1314
+ const json = JSON.parse(rawBody);
1315
+ if (typeof json === "object" && json !== null && !Array.isArray(json)) {
1316
+ const keys = Object.keys(json);
1317
+ if (keys.includes("model") && keys.includes("messages")) {
1318
+ if (!keys.includes("system") && !keys.includes("tools")) {
1319
+ return true;
1320
+ }
1321
+ }
1322
+ }
1323
+ return false;
1324
+ } catch {
1325
+ return false;
1326
+ }
1327
+ }
1328
+ };
1329
+ formatRegistry.register(OpenAIFormatHandler);
1330
+ const DEFAULT_OPENAI_UPSTREAM = "https://api.openai.com/v1";
1331
+ const openaiProvider = {
1332
+ name: "openai",
1333
+ matches(model) {
1334
+ const modelLower = model.toLowerCase();
1335
+ if (modelLower.startsWith("openai-")) {
1336
+ return true;
1337
+ }
1338
+ if (modelLower.includes("gpt-") || modelLower.includes("o1") || modelLower.includes("o2") || modelLower.includes("o3")) {
1339
+ return true;
1340
+ }
1341
+ return false;
1342
+ },
1343
+ getUpstreamBase(_isChatCompletions, providerConfig) {
1344
+ if (providerConfig?.baseUrl !== void 0) {
1345
+ return providerConfig.baseUrl;
1346
+ }
1347
+ return DEFAULT_OPENAI_UPSTREAM;
1348
+ },
1349
+ extractStream(raw, log, fallbackModel, collectChunks) {
1350
+ return extractOpenAIStream(raw, log, fallbackModel, collectChunks);
1351
+ }
1352
+ };
1353
+ registry.register(openaiProvider);
1354
+ const DEFAULT_ALIBABA_UPSTREAM = "https://dashscope.aliyuncs.com/compatible-mode/v1";
1355
+ const alibabaProvider = {
1356
+ name: "alibaba",
1357
+ matches(model) {
1358
+ const m = model.toLowerCase().replace(/\s+/g, "-");
1359
+ if (m.startsWith("glm-5")) {
1360
+ return true;
1361
+ }
1362
+ if (m.startsWith("qwen")) {
1363
+ return true;
1364
+ }
1365
+ return false;
1366
+ },
1367
+ getUpstreamBase(_isChatCompletions, _providerConfig) {
1368
+ return DEFAULT_ALIBABA_UPSTREAM;
1369
+ },
1370
+ extractStream(raw, log, fallbackModel) {
1371
+ return extractOpenAIStream(raw, log, fallbackModel);
1372
+ }
1373
+ };
1374
+ registry.register(alibabaProvider);
1375
+ const ProviderConfigSchema = object({
1376
+ id: string(),
1377
+ name: string(),
1378
+ apiKey: string(),
1379
+ model: string().optional(),
1380
+ /** API format: "anthropic" or "openai" */
1381
+ format: _enum(["anthropic", "openai"]).optional(),
1382
+ /** Base URL for the provider (deprecated - use anthropicBaseUrl/openaiBaseUrl instead) */
1383
+ baseUrl: string().optional(),
1384
+ /** Anthropic API base URL */
1385
+ anthropicBaseUrl: string().optional(),
1386
+ /** OpenAI API base URL */
1387
+ openaiBaseUrl: string().optional(),
1388
+ /** Auth header to use: "bearer" (default) or "x-api-key" */
1389
+ authHeader: _enum(["bearer", "x-api-key"]).optional().default("bearer"),
1390
+ createdAt: string(),
1391
+ updatedAt: string()
1392
+ });
1393
+ object({
1394
+ providers: array(ProviderConfigSchema)
1395
+ });
1396
+ const store = new Conf({
1397
+ projectName: "llm-inspector",
1398
+ defaults: {
1399
+ providers: []
1400
+ }
1401
+ });
1402
+ function migrateProviders() {
1403
+ const providers = store.get("providers", []);
1404
+ let migrated = false;
1405
+ const updated = providers.map((p) => {
1406
+ function getOldUrl(obj, key) {
1407
+ if (obj !== null && typeof obj === "object") {
1408
+ const desc = Object.getOwnPropertyDescriptor(obj, key);
1409
+ if (desc !== void 0 && typeof desc.value === "string") {
1410
+ return desc.value;
1411
+ }
1412
+ }
1413
+ return "";
1414
+ }
1415
+ const oldAnthropicBaseUrl = getOldUrl(p, "anthropicBaseUrl");
1416
+ const oldOpenaiBaseUrl = getOldUrl(p, "openaiBaseUrl");
1417
+ if (p.format !== void 0 && oldAnthropicBaseUrl === "" && oldOpenaiBaseUrl === "") {
1418
+ return p;
1419
+ }
1420
+ const newAnthropicUrl = oldAnthropicBaseUrl !== "" ? oldAnthropicBaseUrl : p.anthropicBaseUrl ?? "";
1421
+ const newOpenaiUrl = oldOpenaiBaseUrl !== "" ? oldOpenaiBaseUrl : p.openaiBaseUrl ?? "";
1422
+ let format;
1423
+ let baseUrl;
1424
+ if (newAnthropicUrl !== "" && newOpenaiUrl !== "") {
1425
+ format = p.format ?? "anthropic";
1426
+ baseUrl = p.baseUrl !== void 0 && p.baseUrl !== "" ? p.baseUrl : newAnthropicUrl;
1427
+ } else if (newOpenaiUrl !== "") {
1428
+ format = "openai";
1429
+ baseUrl = newOpenaiUrl;
1430
+ } else if (newAnthropicUrl !== "") {
1431
+ format = "anthropic";
1432
+ baseUrl = newAnthropicUrl;
1433
+ }
1434
+ migrated = true;
1435
+ return {
1436
+ ...p,
1437
+ format,
1438
+ baseUrl,
1439
+ anthropicBaseUrl: newAnthropicUrl,
1440
+ openaiBaseUrl: newOpenaiUrl
1441
+ };
1442
+ });
1443
+ if (migrated) {
1444
+ store.set("providers", updated);
1445
+ }
1446
+ }
1447
+ migrateProviders();
1448
+ function getProviders() {
1449
+ return store.get("providers", []);
1450
+ }
1451
+ function getProvider(id) {
1452
+ const providers = getProviders();
1453
+ return providers.find((p) => p.id === id);
1454
+ }
1455
+ function normalizeApiKey(apiKey) {
1456
+ return apiKey.replace(/^Bearer\s+/i, "").trim();
1457
+ }
1458
+ function addProvider(name, apiKey, format, baseUrl, model, authHeader) {
1459
+ const providers = getProviders();
1460
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1461
+ const newProvider = {
1462
+ id: randomUUID(),
1463
+ name,
1464
+ apiKey: normalizeApiKey(apiKey),
1465
+ format,
1466
+ baseUrl,
1467
+ model,
1468
+ authHeader: authHeader ?? "bearer",
1469
+ createdAt: now,
1470
+ updatedAt: now
1471
+ };
1472
+ providers.push(newProvider);
1473
+ store.set("providers", providers);
1474
+ return newProvider;
1475
+ }
1476
+ function updateProvider(id, updates) {
1477
+ const providers = getProviders();
1478
+ const existing = providers.find((p) => p.id === id);
1479
+ if (!existing) return null;
1480
+ const updated = {
1481
+ id: existing.id,
1482
+ name: updates.name ?? existing.name,
1483
+ apiKey: updates.apiKey !== void 0 ? normalizeApiKey(updates.apiKey) : existing.apiKey,
1484
+ model: updates.model !== void 0 ? updates.model : existing.model,
1485
+ format: updates.format ?? existing.format,
1486
+ baseUrl: updates.baseUrl !== void 0 ? updates.baseUrl : existing.baseUrl,
1487
+ authHeader: updates.authHeader ?? existing.authHeader,
1488
+ createdAt: existing.createdAt,
1489
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1490
+ };
1491
+ const index = providers.findIndex((p) => p.id === id);
1492
+ providers[index] = updated;
1493
+ store.set("providers", providers);
1494
+ return updated;
1495
+ }
1496
+ function deleteProvider(id) {
1497
+ const providers = getProviders();
1498
+ const filtered = providers.filter((p) => p.id !== id);
1499
+ if (filtered.length === providers.length) return false;
1500
+ store.set("providers", filtered);
1501
+ return true;
1502
+ }
1503
+ function getModelUsageName(model, providerName) {
1504
+ if (providerName !== void 0 && providerName !== "" && providerName.toLowerCase().includes("minimax")) {
1505
+ return model.replace(/ /g, "-");
1506
+ }
1507
+ return model;
1508
+ }
1509
+ function normalizeModelName(name) {
1510
+ return name.toLowerCase().replace(/\s+/g, "-");
1511
+ }
1512
+ function findProviderByModel(model) {
1513
+ const providers = getProviders();
1514
+ const modelLower = model.toLowerCase();
1515
+ const modelNormalized = normalizeModelName(model);
1516
+ for (const provider of providers) {
1517
+ const providerPrefix = (provider.name + "-").toLowerCase();
1518
+ if (modelLower.startsWith(providerPrefix)) {
1519
+ return provider;
1520
+ }
1521
+ if (provider.model !== void 0 && provider.model !== "" && modelNormalized === normalizeModelName(provider.model)) {
1522
+ return provider;
1523
+ }
1524
+ }
1525
+ return null;
1526
+ }
1527
+ const execAsync = promisify(exec);
1528
+ const cache = /* @__PURE__ */ new Map();
1529
+ const CACHE_TTL_MS = 5 * 60 * 1e3;
1530
+ function getFromCache(port) {
1531
+ const entry = cache.get(port);
1532
+ if (entry && Date.now() < entry.expiresAt) {
1533
+ return { port: entry.port, pid: entry.pid, cwd: entry.cwd, projectFolder: entry.projectFolder };
1534
+ }
1535
+ if (entry) cache.delete(port);
1536
+ return null;
1537
+ }
1538
+ function setCache(port, info) {
1539
+ cache.set(port, { ...info, expiresAt: Date.now() + CACHE_TTL_MS });
1540
+ }
1541
+ function extractRemotePort(request) {
1542
+ const socket = request.socket;
1543
+ const remotePort = socket?.remotePort;
1544
+ if (remotePort !== void 0 && remotePort !== null) return remotePort;
1545
+ return null;
1546
+ }
1547
+ async function lookupPidByPort(port) {
1548
+ try {
1549
+ const { stdout } = await execAsync(`netstat -aon | findstr :${port} | findstr ESTABLISHED`, {
1550
+ windowsHide: true
1551
+ });
1552
+ const lines = stdout.trim().split("\n").filter(Boolean);
1553
+ for (const line of lines) {
1554
+ const parts = line.trim().split(/\s+/);
1555
+ const lastPart = parts[parts.length - 1];
1556
+ if (lastPart !== void 0) {
1557
+ const pid = parseInt(lastPart, 10);
1558
+ if (!isNaN(pid) && pid > 0) return pid;
1559
+ }
1560
+ }
1561
+ return null;
1562
+ } catch {
1563
+ return null;
1564
+ }
1565
+ }
1566
+ async function lookupProcessInfo(pid) {
1567
+ try {
1568
+ const { stdout } = await execAsync(
1569
+ `wmic process where processid=${pid} get commandline /value`,
1570
+ { windowsHide: true }
1571
+ );
1572
+ const lines = stdout.trim().split("\n").filter(Boolean);
1573
+ for (const line of lines) {
1574
+ if (line.includes("=")) {
1575
+ const commandLine = (line.split("=")[1] ?? "").trim();
1576
+ if (commandLine) {
1577
+ const exeMatch = commandLine.match(/^"([^"]+)"/) || commandLine.match(/^([^\s]+)/);
1578
+ if (exeMatch && exeMatch[1] !== void 0) {
1579
+ const exePath = exeMatch[1];
1580
+ const exeDir = exePath.substring(0, exePath.lastIndexOf("\\"));
1581
+ return {
1582
+ cwd: exeDir,
1583
+ projectFolder: exeDir.split("\\").pop() ?? null
1584
+ };
1585
+ }
1586
+ }
1587
+ }
1588
+ }
1589
+ return { cwd: null, projectFolder: null };
1590
+ } catch {
1591
+ return { cwd: null, projectFolder: null };
1592
+ }
1593
+ }
1594
+ async function getClientInfo(request) {
1595
+ const port = extractRemotePort(request);
1596
+ if (port === null) {
1597
+ return { port: null, pid: null, cwd: null, projectFolder: null };
1598
+ }
1599
+ const cached = getFromCache(port);
1600
+ if (cached) return cached;
1601
+ const pid = await lookupPidByPort(port);
1602
+ if (pid === null) {
1603
+ const info2 = { port, pid: null, cwd: null, projectFolder: null };
1604
+ setCache(port, info2);
1605
+ return info2;
1606
+ }
1607
+ const { cwd, projectFolder } = await lookupProcessInfo(pid);
1608
+ const info = { port, pid, cwd, projectFolder };
1609
+ setCache(port, info);
1610
+ return info;
1611
+ }
1612
+ function buildProxyHeaders(originalHeaders) {
1613
+ const rawHeaders = {};
1614
+ const headers = new Headers();
1615
+ originalHeaders.forEach((value, key) => {
1616
+ rawHeaders[key.toLowerCase()] = value;
1617
+ });
1618
+ headers.set(HEADER_USER_AGENT, PROXY_IDENTITY);
1619
+ headers.set(HEADER_X_PROXY_IDENTITY, PROXY_IDENTITY);
1620
+ for (const name of PRESERVE_HEADERS) {
1621
+ const value = originalHeaders.get(name);
1622
+ if (value !== null) {
1623
+ headers.set(name, value);
1624
+ }
1625
+ }
1626
+ return { headers, rawHeaders };
1627
+ }
1628
+ function getHostFromUrl$1(urlStr) {
1629
+ try {
1630
+ const url = new URL(urlStr);
1631
+ return url.host;
1632
+ } catch {
1633
+ return "api.anthropic.com";
1634
+ }
1635
+ }
1636
+ function buildFileLogEntry(log, upstreamUrl) {
1637
+ return {
1638
+ timestamp: log.timestamp,
1639
+ id: log.id,
1640
+ method: log.method,
1641
+ path: log.path,
1642
+ model: log.model,
1643
+ sessionId: log.sessionId,
1644
+ rawRequestBody: log.rawRequestBody,
1645
+ responseStatus: log.responseStatus,
1646
+ responseText: log.responseText,
1647
+ inputTokens: log.inputTokens,
1648
+ outputTokens: log.outputTokens,
1649
+ elapsedMs: log.elapsedMs,
1650
+ streaming: log.streaming,
1651
+ userAgent: log.userAgent,
1652
+ origin: log.origin,
1653
+ upstreamUrl,
1654
+ clientPort: log.clientPort,
1655
+ clientPid: log.clientPid,
1656
+ clientCwd: log.clientCwd,
1657
+ clientProjectFolder: log.clientProjectFolder,
1658
+ streamingChunks: log.streamingChunks,
1659
+ streamingChunksPath: log.streamingChunksPath
1660
+ };
1661
+ }
1662
+ function parseRequestPath(req, url) {
1663
+ const apiPath = url.pathname.replace(/^\/proxy/, "") + url.search;
1664
+ const messagesPath = apiPath.split("?")[0] ?? "";
1665
+ const isChatCompletionsV1 = messagesPath === PATH_V1_CHAT_COMPLETIONS;
1666
+ const isChatCompletions = messagesPath === PATH_CHAT_COMPLETIONS || isChatCompletionsV1;
1667
+ const isMessages = req.method === "POST" && (messagesPath === PATH_V1_MESSAGES || messagesPath === PATH_V1_CHAT_COMPLETIONS || messagesPath === PATH_CHAT_COMPLETIONS);
1668
+ const normalizedPath = isChatCompletions && !apiPath.startsWith("/v1/") ? "/v1" + apiPath : apiPath;
1669
+ return {
1670
+ apiPath,
1671
+ messagesPath,
1672
+ isChatCompletionsV1,
1673
+ isChatCompletions,
1674
+ isMessages,
1675
+ normalizedPath
1676
+ };
1677
+ }
1678
+ function buildUpstreamUrl$1(upstreamBase, normalizedPath) {
1679
+ return upstreamBase + normalizedPath;
1680
+ }
1681
+ function selectUpstreamBase$1(isChatCompletions, matchedProviderConfig) {
1682
+ let upstreamBase;
1683
+ if (matchedProviderConfig) {
1684
+ if (isChatCompletions && matchedProviderConfig.openaiBaseUrl !== void 0 && matchedProviderConfig.openaiBaseUrl !== "") {
1685
+ upstreamBase = matchedProviderConfig.openaiBaseUrl;
1686
+ } else if (!isChatCompletions && matchedProviderConfig.anthropicBaseUrl !== void 0 && matchedProviderConfig.anthropicBaseUrl !== "") {
1687
+ upstreamBase = matchedProviderConfig.anthropicBaseUrl;
1688
+ } else if (matchedProviderConfig.baseUrl !== void 0 && matchedProviderConfig.baseUrl !== "") {
1689
+ upstreamBase = matchedProviderConfig.baseUrl;
1690
+ } else {
1691
+ upstreamBase = matchedProviderConfig.format === "openai" ? DEFAULT_OPENAI_UPSTREAM$1 : DEFAULT_UPSTREAM$1;
1692
+ }
1693
+ } else {
1694
+ upstreamBase = isChatCompletions ? DEFAULT_OPENAI_UPSTREAM$1 : DEFAULT_UPSTREAM$1;
1695
+ }
1696
+ return upstreamBase;
1697
+ }
1698
+ function injectAuthHeaders$1(upstreamHeaders, matchedProviderConfig) {
1699
+ if (!matchedProviderConfig) return;
1700
+ const apiKey = matchedProviderConfig.apiKey.replace(/^Bearer\s+/i, "").trim();
1701
+ if (matchedProviderConfig.authHeader === AUTH_HEADER_X_API_KEY) {
1702
+ upstreamHeaders.set(HEADER_X_API_KEY, apiKey);
1703
+ upstreamHeaders.delete(HEADER_AUTHORIZATION);
1704
+ } else {
1705
+ upstreamHeaders.set(HEADER_AUTHORIZATION, `Bearer ${apiKey}`);
1706
+ }
1707
+ }
1708
+ function handleNonStreamingResponse(upstreamRes, responseBody, startTime, formatHandler, upstreamUrl, log) {
1709
+ const elapsedMs = Date.now() - startTime;
1710
+ const tokens = formatHandler.extractTokens(responseBody);
1711
+ log.elapsedMs = elapsedMs;
1712
+ log.responseStatus = upstreamRes.status;
1713
+ log.responseText = responseBody;
1714
+ log.inputTokens = tokens.inputTokens;
1715
+ log.outputTokens = tokens.outputTokens;
1716
+ log.cacheCreationInputTokens = tokens.cacheCreationInputTokens;
1717
+ log.cacheReadInputTokens = tokens.cacheReadInputTokens;
1718
+ appendLogEntry({ ...buildFileLogEntry(log, upstreamUrl), error: null });
1719
+ const responseHeaders = new Headers(upstreamRes.headers);
1720
+ responseHeaders.delete(HEADER_CONTENT_ENCODING);
1721
+ responseHeaders.delete(HEADER_CONTENT_LENGTH);
1722
+ return new Response(responseBody, {
1723
+ status: upstreamRes.status,
1724
+ headers: responseHeaders
1725
+ });
1726
+ }
1727
+ function handleStreamingResponse(upstreamRes, req, startTime, formatHandler, upstreamUrl, log) {
1728
+ log.streaming = true;
1729
+ log.responseStatus = upstreamRes.status;
1730
+ const chunks = [];
1731
+ const decoder = new TextDecoder();
1732
+ const transform = new TransformStream({
1733
+ transform(chunk, controller) {
1734
+ controller.enqueue(chunk);
1735
+ chunks.push(decoder.decode(chunk, { stream: true }));
1736
+ },
1737
+ flush() {
1738
+ const full = chunks.join("");
1739
+ log.elapsedMs = Date.now() - startTime;
1740
+ log.responseText = formatHandler.extractStream(full, log, log.model ?? void 0, true);
1741
+ if (log.streamingChunks && log.streamingChunks.chunks.length > 0) {
1742
+ const chunkPath = writeChunks(
1743
+ log.id,
1744
+ log.streamingChunks.chunks,
1745
+ log.streamingChunks.truncated
1746
+ );
1747
+ log.streamingChunksPath = chunkPath;
1748
+ }
1749
+ appendLogEntry({ ...buildFileLogEntry(log, upstreamUrl), error: null });
1750
+ emitLogUpdate(log);
1751
+ }
1752
+ });
1753
+ if (upstreamRes.body === null) {
1754
+ return new Response("No response body", { status: STATUS_BAD_GATEWAY });
1755
+ }
1756
+ const loggedStream = upstreamRes.body.pipeThrough(transform);
1757
+ req.signal?.addEventListener("abort", () => {
1758
+ if (log.responseText === null && chunks.length > 0) {
1759
+ const full = chunks.join("");
1760
+ log.elapsedMs = Date.now() - startTime;
1761
+ log.responseText = formatHandler.extractStream(full, log, log.model ?? void 0, true);
1762
+ if (log.streamingChunks && log.streamingChunks.chunks.length > 0) {
1763
+ const chunkPath = writeChunks(
1764
+ log.id,
1765
+ log.streamingChunks.chunks,
1766
+ log.streamingChunks.truncated
1767
+ );
1768
+ log.streamingChunksPath = chunkPath;
1769
+ }
1770
+ appendLogEntry({ ...buildFileLogEntry(log, upstreamUrl), error: "Client aborted" });
1771
+ emitLogUpdate(log);
1772
+ }
1773
+ });
1774
+ const responseHeaders = new Headers(upstreamRes.headers);
1775
+ responseHeaders.delete(HEADER_CONTENT_ENCODING);
1776
+ responseHeaders.delete(HEADER_CONTENT_LENGTH);
1777
+ return new Response(loggedStream, {
1778
+ status: upstreamRes.status,
1779
+ headers: responseHeaders
1780
+ });
1781
+ }
1782
+ async function handleProxy(req) {
1783
+ const url = new URL(req.url);
1784
+ const parsed = parseRequestPath(req, url);
1785
+ let requestBody = null;
1786
+ if (req.body && req.method !== "GET" && req.method !== "HEAD") {
1787
+ requestBody = await req.text();
1788
+ }
1789
+ const model = requestBody !== null ? extractModelFromBody(requestBody) : null;
1790
+ const matchedProviderConfig = findProviderByModelFromConfig(requestBody);
1791
+ const upstreamBase = selectUpstreamBase$1(parsed.isChatCompletions, matchedProviderConfig);
1792
+ const upstreamUrl = buildUpstreamUrl$1(upstreamBase, parsed.normalizedPath);
1793
+ const upstreamHost = getHostFromUrl$1(upstreamBase);
1794
+ const startTime = Date.now();
1795
+ const { headers: upstreamHeaders, rawHeaders } = buildProxyHeaders(req.headers);
1796
+ upstreamHeaders.set(HEADER_HOST, upstreamHost);
1797
+ injectAuthHeaders$1(upstreamHeaders, matchedProviderConfig);
1798
+ const provider = model !== null ? registry.findProvider(model) : null;
1799
+ if (model === null || provider === null) {
1800
+ return new Response("Forbidden: unsupported provider", { status: STATUS_FORBIDDEN });
1801
+ }
1802
+ let formatHandler;
1803
+ if (matchedProviderConfig?.format) {
1804
+ formatHandler = formatRegistry.get(matchedProviderConfig.format) ?? null;
1805
+ } else {
1806
+ formatHandler = formatForPath(parsed.apiPath);
1807
+ }
1808
+ if (formatHandler === null) {
1809
+ return new Response("Forbidden: unsupported format", { status: STATUS_FORBIDDEN });
1810
+ }
1811
+ const clientInfo = await getClientInfo(req);
1812
+ const upstreamHeadersObj = {};
1813
+ upstreamHeaders.forEach((value, key) => {
1814
+ upstreamHeadersObj[key.toLowerCase()] = value;
1815
+ });
1816
+ const log = await createLog(
1817
+ req.method,
1818
+ parsed.apiPath,
1819
+ requestBody,
1820
+ req.headers,
1821
+ clientInfo,
1822
+ rawHeaders,
1823
+ upstreamHeadersObj,
1824
+ formatHandler.format
1825
+ );
1826
+ let upstreamRes;
1827
+ try {
1828
+ upstreamRes = await fetch(upstreamUrl, {
1829
+ method: req.method,
1830
+ headers: upstreamHeaders,
1831
+ body: requestBody
1832
+ });
1833
+ } catch (err) {
1834
+ log.elapsedMs = Date.now() - startTime;
1835
+ log.responseStatus = STATUS_BAD_GATEWAY;
1836
+ log.responseText = String(err);
1837
+ appendLogEntry({ ...buildFileLogEntry(log, upstreamUrl), error: String(err) });
1838
+ return new Response(`Proxy error: ${err}`, { status: STATUS_BAD_GATEWAY });
1839
+ }
1840
+ const isStream = upstreamRes.headers.get(HEADER_CONTENT_TYPE)?.includes(CONTENT_TYPE_EVENT_STREAM) ?? false;
1841
+ if (!isStream) {
1842
+ const responseBody = await upstreamRes.text();
1843
+ return handleNonStreamingResponse(
1844
+ upstreamRes,
1845
+ responseBody,
1846
+ startTime,
1847
+ formatHandler,
1848
+ upstreamUrl,
1849
+ log
1850
+ );
1851
+ }
1852
+ return handleStreamingResponse(upstreamRes, req, startTime, formatHandler, upstreamUrl, log);
1853
+ }
1854
+ function findProviderByModelFromConfig(requestBody) {
1855
+ if (requestBody === null) return null;
1856
+ const model = extractModelFromBody(requestBody);
1857
+ if (model === null) return null;
1858
+ return findProviderByModel(model);
1859
+ }
1860
+ const Route$c = createFileRoute("/proxy/$")({
1861
+ server: {
1862
+ handlers: {
1863
+ GET: ({ request }) => handleProxy(request),
1864
+ POST: ({ request }) => handleProxy(request),
1865
+ PUT: ({ request }) => handleProxy(request),
1866
+ DELETE: ({ request }) => handleProxy(request),
1867
+ PATCH: ({ request }) => handleProxy(request),
1868
+ OPTIONS: ({ request }) => handleProxy(request)
1869
+ }
1870
+ }
1871
+ });
1872
+ const Route$b = createFileRoute("/api/sessions")({
1873
+ server: {
1874
+ handlers: {
1875
+ GET: () => Response.json(getSessions())
1876
+ }
1877
+ }
1878
+ });
1879
+ const ProviderInputSchema = object({
1880
+ name: string().min(1, "Name is required"),
1881
+ apiKey: string().min(1, "API key is required"),
1882
+ format: _enum(["anthropic", "openai"]),
1883
+ baseUrl: string().min(1, "Base URL is required"),
1884
+ model: string().min(1, "Model is required"),
1885
+ authHeader: _enum(["bearer", "x-api-key"]).optional().default("bearer")
1886
+ });
1887
+ const Route$a = createFileRoute("/api/providers")({
1888
+ server: {
1889
+ handlers: {
1890
+ GET: () => {
1891
+ return Response.json(getProviders());
1892
+ },
1893
+ POST: async ({ request }) => {
1894
+ const parsed = ProviderInputSchema.safeParse(await request.json());
1895
+ if (!parsed.success) {
1896
+ return Response.json({ error: parsed.error.message }, { status: 400 });
1897
+ }
1898
+ const newProvider = addProvider(
1899
+ parsed.data.name,
1900
+ parsed.data.apiKey,
1901
+ parsed.data.format,
1902
+ parsed.data.baseUrl,
1903
+ parsed.data.model,
1904
+ parsed.data.authHeader
1905
+ );
1906
+ return Response.json(newProvider, { status: 201 });
1907
+ }
1908
+ }
1909
+ }
1910
+ });
1911
+ const Route$9 = createFileRoute("/api/models")({
1912
+ server: {
1913
+ handlers: {
1914
+ GET: () => Response.json(getModels())
1915
+ }
1916
+ }
1917
+ });
1918
+ const Route$8 = createFileRoute("/api/logs")({
1919
+ server: {
1920
+ handlers: {
1921
+ GET: ({ request }) => {
1922
+ const url = new URL(request.url);
1923
+ const sessionId = url.searchParams.get("sessionId") ?? void 0;
1924
+ const model = url.searchParams.get("model") ?? void 0;
1925
+ const offset = Number(url.searchParams.get("offset") ?? 0);
1926
+ const limit = Number(url.searchParams.get("limit") ?? 50);
1927
+ const allLogs = getFilteredLogs(sessionId, model);
1928
+ const paginatedLogs = allLogs.slice(offset, offset + limit);
1929
+ return Response.json({
1930
+ logs: paginatedLogs,
1931
+ total: allLogs.length,
1932
+ offset,
1933
+ limit
1934
+ });
1935
+ },
1936
+ DELETE: () => {
1937
+ const result = clearAllLogs();
1938
+ return Response.json({ success: true, cleared: result.cleared });
1939
+ }
1940
+ }
1941
+ }
1942
+ });
1943
+ const Route$7 = createFileRoute("/api/health")({
1944
+ server: {
1945
+ handlers: {
1946
+ GET: () => {
1947
+ return Response.json({ status: "ok" });
1948
+ }
1949
+ }
1950
+ }
1951
+ });
1952
+ const ProviderUpdateSchema = object({
1953
+ name: string().min(1, "Name is required").optional(),
1954
+ apiKey: string().min(1, "API key is required").optional(),
1955
+ format: _enum(["anthropic", "openai"]).optional(),
1956
+ baseUrl: string().min(1, "Base URL is required").optional(),
1957
+ model: string().min(1, "Model is required").optional(),
1958
+ authHeader: _enum(["bearer", "x-api-key"]).optional()
1959
+ });
1960
+ const Route$6 = createFileRoute("/api/providers/$providerId")({
1961
+ server: {
1962
+ handlers: {
1963
+ GET: ({ params }) => {
1964
+ const providers = getProviders();
1965
+ const provider = providers.find((p) => p.id === params.providerId);
1966
+ if (!provider) {
1967
+ return Response.json({ error: "Provider not found" }, { status: 404 });
1968
+ }
1969
+ return Response.json(provider);
1970
+ },
1971
+ PUT: async ({ params, request }) => {
1972
+ const parsed = ProviderUpdateSchema.safeParse(await request.json());
1973
+ if (!parsed.success) {
1974
+ return Response.json({ error: parsed.error.message }, { status: 400 });
1975
+ }
1976
+ const updated = updateProvider(params.providerId, parsed.data);
1977
+ if (!updated) {
1978
+ return Response.json({ error: "Provider not found" }, { status: 404 });
1979
+ }
1980
+ return Response.json(updated);
1981
+ },
1982
+ DELETE: ({ params }) => {
1983
+ const deleted = deleteProvider(params.providerId);
1984
+ if (!deleted) {
1985
+ return Response.json({ error: "Provider not found" }, { status: 404 });
1986
+ }
1987
+ return Response.json({ success: true });
1988
+ }
1989
+ }
1990
+ }
1991
+ });
1992
+ const Route$5 = createFileRoute("/api/logs/stream")({
1993
+ server: {
1994
+ handlers: {
1995
+ GET: ({ request }) => {
1996
+ const url = new URL(request.url);
1997
+ const sessionId = url.searchParams.get("sessionId") ?? void 0;
1998
+ const model = url.searchParams.get("model") ?? void 0;
1999
+ let controllerRef = null;
2000
+ const heartbeat = setInterval(() => {
2001
+ if (controllerRef) {
2002
+ try {
2003
+ controllerRef.enqueue(new TextEncoder().encode(": heartbeat\n\n"));
2004
+ } catch {
2005
+ }
2006
+ }
2007
+ }, 3e4);
2008
+ const unsubscribe = onLogUpdate((log) => {
2009
+ if (!controllerRef) return;
2010
+ if (sessionId !== void 0 && log.sessionId !== sessionId) return;
2011
+ if (model !== void 0 && log.model !== model) return;
2012
+ try {
2013
+ const data = `data: ${JSON.stringify({ type: "update", log })}
2014
+
2015
+ `;
2016
+ controllerRef.enqueue(new TextEncoder().encode(data));
2017
+ } catch {
2018
+ }
2019
+ });
2020
+ const stream = new ReadableStream({
2021
+ start(controller) {
2022
+ controllerRef = controller;
2023
+ const logs = getFilteredLogs(sessionId, model);
2024
+ const initData = `data: ${JSON.stringify({ type: "init", logs })}
2025
+
2026
+ `;
2027
+ controller.enqueue(new TextEncoder().encode(initData));
2028
+ },
2029
+ cancel() {
2030
+ clearInterval(heartbeat);
2031
+ unsubscribe();
2032
+ controllerRef = null;
2033
+ }
2034
+ });
2035
+ return new Response(stream, {
2036
+ headers: {
2037
+ "Content-Type": "text/event-stream",
2038
+ "Cache-Control": "no-cache",
2039
+ Connection: "keep-alive"
2040
+ }
2041
+ });
2042
+ }
2043
+ }
2044
+ }
2045
+ });
2046
+ const Route$4 = createFileRoute("/api/logs/$id")({
2047
+ server: {
2048
+ handlers: {
2049
+ GET: async ({ params }) => {
2050
+ const id = Number(params.id);
2051
+ if (isNaN(id)) {
2052
+ return Response.json({ error: "Invalid ID" }, { status: 400 });
2053
+ }
2054
+ const log = await getLogById(id);
2055
+ if (log === null) {
2056
+ return Response.json({ error: "Log not found" }, { status: 404 });
2057
+ }
2058
+ return Response.json(log);
2059
+ }
2060
+ }
2061
+ }
2062
+ });
2063
+ const Route$3 = createFileRoute("/api/config/paths")({
2064
+ server: {
2065
+ handlers: {
2066
+ GET: () => {
2067
+ return Response.json({
2068
+ providerConfig: store.path
2069
+ });
2070
+ }
2071
+ }
2072
+ }
2073
+ });
2074
+ const AnthropicResponseSchema = object({
2075
+ id: string().optional(),
2076
+ type: string().optional(),
2077
+ model: string().optional(),
2078
+ usage: object({
2079
+ input_tokens: number().optional(),
2080
+ output_tokens: number().optional()
2081
+ }).optional(),
2082
+ content: array(
2083
+ discriminatedUnion("type", [
2084
+ object({ type: literal("text"), text: string() }),
2085
+ object({ type: literal("thinking"), thinking: string() })
2086
+ ])
2087
+ )
2088
+ });
2089
+ const OpenAIResponseSchema = object({
2090
+ id: string().optional(),
2091
+ model: string().optional(),
2092
+ usage: object({
2093
+ prompt_tokens: number().optional(),
2094
+ completion_tokens: number().optional()
2095
+ }).optional(),
2096
+ choices: array(
2097
+ object({
2098
+ message: object({
2099
+ role: string(),
2100
+ content: string().nullable()
2101
+ }),
2102
+ finish_reason: string().nullable()
2103
+ })
2104
+ )
2105
+ });
2106
+ async function testEndpoint(baseUrl, apiKey, path2, model, isOpenAI) {
2107
+ const startTime = Date.now();
2108
+ const body = JSON.stringify({
2109
+ model,
2110
+ messages: [{ role: "user", content: "say hello and briefly introduce yourself" }],
2111
+ max_tokens: 1024
2112
+ });
2113
+ try {
2114
+ const url = `${baseUrl}${path2}`;
2115
+ const response = await fetch(url, {
2116
+ method: "POST",
2117
+ headers: {
2118
+ "Content-Type": "application/json",
2119
+ Authorization: `Bearer ${apiKey}`
2120
+ },
2121
+ body
2122
+ });
2123
+ const latencyMs = Date.now() - startTime;
2124
+ const responseText = await response.text();
2125
+ if (!response.ok) {
2126
+ return {
2127
+ success: false,
2128
+ error: `HTTP ${response.status}: ${responseText.slice(0, 200)}`,
2129
+ latencyMs
2130
+ };
2131
+ }
2132
+ try {
2133
+ if (isOpenAI) {
2134
+ const json = OpenAIResponseSchema.parse(JSON.parse(responseText));
2135
+ const responseModel = json.model ?? model;
2136
+ const inputTokens = json.usage?.prompt_tokens;
2137
+ const outputTokens = json.usage?.completion_tokens;
2138
+ const content = json.choices?.[0]?.message?.content ?? null;
2139
+ return {
2140
+ success: true,
2141
+ model: responseModel,
2142
+ inputTokens,
2143
+ outputTokens,
2144
+ latencyMs,
2145
+ content: content !== null && content !== "" ? [{ type: "text", text: content }] : void 0,
2146
+ rawResponse: responseText
2147
+ };
2148
+ } else {
2149
+ const json = AnthropicResponseSchema.parse(JSON.parse(responseText));
2150
+ const responseModel = json.model ?? model;
2151
+ const inputTokens = json.usage?.input_tokens;
2152
+ const outputTokens = json.usage?.output_tokens;
2153
+ const content = [];
2154
+ for (const block of json.content ?? []) {
2155
+ if (block.type === "text" && block.text) {
2156
+ content.push({ type: "text", text: block.text });
2157
+ } else if (block.type === "thinking" && block.thinking) {
2158
+ content.push({ type: "thinking", thinking: block.thinking });
2159
+ }
2160
+ }
2161
+ return {
2162
+ success: true,
2163
+ model: responseModel,
2164
+ inputTokens,
2165
+ outputTokens,
2166
+ latencyMs,
2167
+ content: content.length > 0 ? content : void 0,
2168
+ rawResponse: responseText
2169
+ };
2170
+ }
2171
+ } catch (parseErr) {
2172
+ return {
2173
+ success: true,
2174
+ model,
2175
+ latencyMs,
2176
+ content: [{ type: "text", text: responseText.slice(0, 2e3) }],
2177
+ rawResponse: responseText
2178
+ };
2179
+ }
2180
+ } catch (err) {
2181
+ return {
2182
+ success: false,
2183
+ error: String(err),
2184
+ latencyMs: Date.now() - startTime
2185
+ };
2186
+ }
2187
+ }
2188
+ async function testStreamingEndpoint(baseUrl, apiKey, path2, model, isOpenAI) {
2189
+ const startTime = Date.now();
2190
+ const body = JSON.stringify({
2191
+ model,
2192
+ messages: [{ role: "user", content: "say hello" }],
2193
+ max_tokens: 256,
2194
+ stream: true
2195
+ });
2196
+ try {
2197
+ const url = `${baseUrl}${path2}`;
2198
+ const response = await fetch(url, {
2199
+ method: "POST",
2200
+ headers: {
2201
+ "Content-Type": "application/json",
2202
+ Authorization: `Bearer ${apiKey}`
2203
+ },
2204
+ body
2205
+ });
2206
+ const latencyMs = Date.now() - startTime;
2207
+ if (!response.ok) {
2208
+ const responseText = await response.text();
2209
+ return {
2210
+ success: false,
2211
+ error: `HTTP ${response.status}: ${responseText.slice(0, 200)}`,
2212
+ latencyMs,
2213
+ streaming: true
2214
+ };
2215
+ }
2216
+ const chunks = [];
2217
+ const reader = response.body?.getReader();
2218
+ if (!reader) {
2219
+ return {
2220
+ success: false,
2221
+ error: "No response body",
2222
+ latencyMs,
2223
+ streaming: true
2224
+ };
2225
+ }
2226
+ const decoder = new TextDecoder();
2227
+ try {
2228
+ while (true) {
2229
+ const { done, value } = await reader.read();
2230
+ if (done) break;
2231
+ chunks.push(decoder.decode(value, { stream: true }));
2232
+ }
2233
+ } catch (readErr) {
2234
+ return {
2235
+ success: false,
2236
+ error: `Stream read error: ${readErr}`,
2237
+ latencyMs,
2238
+ streaming: true
2239
+ };
2240
+ }
2241
+ const fullResponse = chunks.join("");
2242
+ const mockLog = {
2243
+ id: 0,
2244
+ timestamp: "",
2245
+ method: "POST",
2246
+ path: "",
2247
+ model: null,
2248
+ sessionId: null,
2249
+ rawRequestBody: null,
2250
+ responseStatus: null,
2251
+ responseText: null,
2252
+ inputTokens: null,
2253
+ outputTokens: null,
2254
+ cacheCreationInputTokens: null,
2255
+ cacheReadInputTokens: null,
2256
+ elapsedMs: 0,
2257
+ streaming: true,
2258
+ userAgent: null,
2259
+ origin: null,
2260
+ apiFormat: "anthropic",
2261
+ isTest: true
2262
+ };
2263
+ let reconstructedJson;
2264
+ if (isOpenAI) {
2265
+ reconstructedJson = extractOpenAIStream(fullResponse, mockLog, model, true);
2266
+ } else {
2267
+ reconstructedJson = extractAnthropicStream(fullResponse, mockLog, model, true);
2268
+ }
2269
+ const streamingChunks = mockLog.streamingChunks ?? void 0;
2270
+ try {
2271
+ let content = [];
2272
+ let inputTokens;
2273
+ let outputTokens;
2274
+ let responseModel;
2275
+ if (isOpenAI) {
2276
+ const parsed = OpenAIResponseSchema.parse(JSON.parse(reconstructedJson));
2277
+ responseModel = typeof parsed.model === "string" && parsed.model !== "" ? parsed.model : void 0;
2278
+ inputTokens = parsed.usage?.prompt_tokens;
2279
+ outputTokens = parsed.usage?.completion_tokens;
2280
+ const textContent = parsed.choices?.[0]?.message?.content;
2281
+ if (textContent !== null && textContent !== "") {
2282
+ content = [{ type: "text", text: textContent }];
2283
+ }
2284
+ } else {
2285
+ const parsed = AnthropicResponseSchema.parse(JSON.parse(reconstructedJson));
2286
+ responseModel = typeof parsed.model === "string" && parsed.model !== "" ? parsed.model : void 0;
2287
+ inputTokens = parsed.usage?.input_tokens;
2288
+ outputTokens = parsed.usage?.output_tokens;
2289
+ for (const block of parsed.content ?? []) {
2290
+ if (block.type === "text" && block.text) {
2291
+ content.push({ type: "text", text: block.text });
2292
+ } else if (block.type === "thinking" && block.thinking) {
2293
+ content.push({ type: "thinking", thinking: block.thinking });
2294
+ }
2295
+ }
2296
+ }
2297
+ return {
2298
+ success: true,
2299
+ model: responseModel ?? model,
2300
+ inputTokens,
2301
+ outputTokens,
2302
+ latencyMs,
2303
+ content: content.length > 0 ? content : void 0,
2304
+ rawResponse: reconstructedJson,
2305
+ streaming: true,
2306
+ streamingChunks
2307
+ };
2308
+ } catch {
2309
+ return {
2310
+ success: true,
2311
+ model,
2312
+ latencyMs,
2313
+ content: [{ type: "text", text: reconstructedJson.slice(0, 2e3) }],
2314
+ rawResponse: reconstructedJson,
2315
+ streaming: true,
2316
+ streamingChunks
2317
+ };
2318
+ }
2319
+ } catch (err) {
2320
+ return {
2321
+ success: false,
2322
+ error: String(err),
2323
+ latencyMs: Date.now() - startTime,
2324
+ streaming: true
2325
+ };
2326
+ }
2327
+ }
2328
+ function createTestLogEntry(providerName, path2, body, upstreamUrl, result, isTest) {
2329
+ return {
2330
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2331
+ id: `test-${Date.now()}`,
2332
+ method: "POST",
2333
+ path: path2,
2334
+ model: isTest ? result.model : void 0,
2335
+ sessionId: null,
2336
+ rawRequestBody: body,
2337
+ responseStatus: result.success ? 200 : 500,
2338
+ responseText: result.rawResponse ?? JSON.stringify(result),
2339
+ inputTokens: result.inputTokens ?? null,
2340
+ outputTokens: result.outputTokens ?? null,
2341
+ elapsedMs: result.latencyMs ?? 0,
2342
+ streaming: result.streaming ?? false,
2343
+ userAgent: "provider-test",
2344
+ origin: null,
2345
+ upstreamUrl,
2346
+ error: result.success ? null : result.error,
2347
+ isTest: true,
2348
+ providerName
2349
+ };
2350
+ }
2351
+ const Route$2 = createFileRoute("/api/providers/$providerId/test")({
2352
+ server: {
2353
+ handlers: {
2354
+ POST: async ({ params }) => {
2355
+ const provider = getProvider(params.providerId);
2356
+ if (!provider) {
2357
+ return Response.json({ error: "Provider not found" }, { status: 404 });
2358
+ }
2359
+ if (provider.model === void 0 || provider.model.trim().length === 0) {
2360
+ return Response.json(
2361
+ { error: "Please configure a model name in provider settings" },
2362
+ { status: 400 }
2363
+ );
2364
+ }
2365
+ const model = provider.model.trim();
2366
+ const usageModel = getModelUsageName(model, provider.name);
2367
+ const results = {
2368
+ anthropic: { nonStreaming: { notConfigured: true }, streaming: { notConfigured: true } },
2369
+ openai: { nonStreaming: { notConfigured: true }, streaming: { notConfigured: true } }
2370
+ };
2371
+ if (provider.anthropicBaseUrl !== void 0 && provider.anthropicBaseUrl.length > 0) {
2372
+ const nonStreamingResult = await testEndpoint(
2373
+ provider.anthropicBaseUrl,
2374
+ provider.apiKey,
2375
+ "/v1/messages",
2376
+ usageModel,
2377
+ false
2378
+ );
2379
+ results.anthropic.nonStreaming = nonStreamingResult;
2380
+ const requestBody = JSON.stringify({
2381
+ model: usageModel,
2382
+ messages: [{ role: "user", content: "say hello and briefly introduce yourself" }],
2383
+ max_tokens: 1024
2384
+ });
2385
+ const upstreamUrl = `${provider.anthropicBaseUrl}/v1/messages`;
2386
+ await addTestLogEntry({
2387
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2388
+ method: "POST",
2389
+ path: "/v1/messages",
2390
+ model: nonStreamingResult.model ?? model,
2391
+ sessionId: null,
2392
+ rawRequestBody: requestBody,
2393
+ responseStatus: nonStreamingResult.success ? 200 : 500,
2394
+ responseText: nonStreamingResult.rawResponse ?? JSON.stringify(nonStreamingResult),
2395
+ inputTokens: nonStreamingResult.inputTokens ?? null,
2396
+ outputTokens: nonStreamingResult.outputTokens ?? null,
2397
+ cacheCreationInputTokens: null,
2398
+ cacheReadInputTokens: null,
2399
+ elapsedMs: nonStreamingResult.latencyMs ?? 0,
2400
+ streaming: false,
2401
+ userAgent: "provider-test",
2402
+ origin: null,
2403
+ apiFormat: "anthropic",
2404
+ isTest: true,
2405
+ providerName: provider.name
2406
+ });
2407
+ appendLogEntry(
2408
+ createTestLogEntry(
2409
+ provider.name,
2410
+ "/v1/messages",
2411
+ requestBody,
2412
+ upstreamUrl,
2413
+ nonStreamingResult,
2414
+ true
2415
+ )
2416
+ );
2417
+ const streamingResult = await testStreamingEndpoint(
2418
+ provider.anthropicBaseUrl,
2419
+ provider.apiKey,
2420
+ "/v1/messages",
2421
+ usageModel,
2422
+ false
2423
+ );
2424
+ results.anthropic.streaming = streamingResult;
2425
+ const streamingRequestBody = JSON.stringify({
2426
+ model: usageModel,
2427
+ messages: [{ role: "user", content: "say hello" }],
2428
+ max_tokens: 256,
2429
+ stream: true
2430
+ });
2431
+ await addTestLogEntry({
2432
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2433
+ method: "POST",
2434
+ path: "/v1/messages",
2435
+ model: streamingResult.model ?? model,
2436
+ sessionId: null,
2437
+ rawRequestBody: streamingRequestBody,
2438
+ responseStatus: streamingResult.success ? 200 : 500,
2439
+ responseText: streamingResult.rawResponse ?? JSON.stringify(streamingResult),
2440
+ inputTokens: streamingResult.inputTokens ?? null,
2441
+ outputTokens: streamingResult.outputTokens ?? null,
2442
+ cacheCreationInputTokens: null,
2443
+ cacheReadInputTokens: null,
2444
+ elapsedMs: streamingResult.latencyMs ?? 0,
2445
+ streaming: true,
2446
+ streamingChunks: streamingResult.streamingChunks,
2447
+ userAgent: "provider-test",
2448
+ origin: null,
2449
+ apiFormat: "anthropic",
2450
+ isTest: true,
2451
+ providerName: provider.name
2452
+ });
2453
+ appendLogEntry(
2454
+ createTestLogEntry(
2455
+ provider.name,
2456
+ "/v1/messages",
2457
+ streamingRequestBody,
2458
+ upstreamUrl,
2459
+ streamingResult,
2460
+ true
2461
+ )
2462
+ );
2463
+ }
2464
+ if (provider.openaiBaseUrl !== void 0 && provider.openaiBaseUrl.length > 0) {
2465
+ const nonStreamingResult = await testEndpoint(
2466
+ provider.openaiBaseUrl,
2467
+ provider.apiKey,
2468
+ "/v1/chat/completions",
2469
+ usageModel,
2470
+ true
2471
+ );
2472
+ results.openai.nonStreaming = nonStreamingResult;
2473
+ const requestBody = JSON.stringify({
2474
+ model: usageModel,
2475
+ messages: [{ role: "user", content: "say hello and briefly introduce yourself" }],
2476
+ max_tokens: 1024
2477
+ });
2478
+ const upstreamUrl = `${provider.openaiBaseUrl}/v1/chat/completions`;
2479
+ await addTestLogEntry({
2480
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2481
+ method: "POST",
2482
+ path: "/v1/chat/completions",
2483
+ model: nonStreamingResult.model ?? model,
2484
+ sessionId: null,
2485
+ rawRequestBody: requestBody,
2486
+ responseStatus: nonStreamingResult.success ? 200 : 500,
2487
+ responseText: nonStreamingResult.rawResponse ?? JSON.stringify(nonStreamingResult),
2488
+ inputTokens: nonStreamingResult.inputTokens ?? null,
2489
+ outputTokens: nonStreamingResult.outputTokens ?? null,
2490
+ cacheCreationInputTokens: null,
2491
+ cacheReadInputTokens: null,
2492
+ elapsedMs: nonStreamingResult.latencyMs ?? 0,
2493
+ streaming: false,
2494
+ userAgent: "provider-test",
2495
+ origin: null,
2496
+ apiFormat: "openai",
2497
+ isTest: true,
2498
+ providerName: provider.name
2499
+ });
2500
+ appendLogEntry(
2501
+ createTestLogEntry(
2502
+ provider.name,
2503
+ "/v1/chat/completions",
2504
+ requestBody,
2505
+ upstreamUrl,
2506
+ nonStreamingResult,
2507
+ true
2508
+ )
2509
+ );
2510
+ const streamingResult = await testStreamingEndpoint(
2511
+ provider.openaiBaseUrl,
2512
+ provider.apiKey,
2513
+ "/v1/chat/completions",
2514
+ usageModel,
2515
+ true
2516
+ );
2517
+ results.openai.streaming = streamingResult;
2518
+ const streamingRequestBody = JSON.stringify({
2519
+ model: usageModel,
2520
+ messages: [{ role: "user", content: "say hello" }],
2521
+ max_tokens: 256,
2522
+ stream: true
2523
+ });
2524
+ await addTestLogEntry({
2525
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2526
+ method: "POST",
2527
+ path: "/v1/chat/completions",
2528
+ model: streamingResult.model ?? model,
2529
+ sessionId: null,
2530
+ rawRequestBody: streamingRequestBody,
2531
+ responseStatus: streamingResult.success ? 200 : 500,
2532
+ responseText: streamingResult.rawResponse ?? JSON.stringify(streamingResult),
2533
+ inputTokens: streamingResult.inputTokens ?? null,
2534
+ outputTokens: streamingResult.outputTokens ?? null,
2535
+ cacheCreationInputTokens: null,
2536
+ cacheReadInputTokens: null,
2537
+ elapsedMs: streamingResult.latencyMs ?? 0,
2538
+ streaming: true,
2539
+ streamingChunks: streamingResult.streamingChunks,
2540
+ userAgent: "provider-test",
2541
+ origin: null,
2542
+ apiFormat: "openai",
2543
+ isTest: true,
2544
+ providerName: provider.name
2545
+ });
2546
+ appendLogEntry(
2547
+ createTestLogEntry(
2548
+ provider.name,
2549
+ "/v1/chat/completions",
2550
+ streamingRequestBody,
2551
+ upstreamUrl,
2552
+ streamingResult,
2553
+ true
2554
+ )
2555
+ );
2556
+ }
2557
+ return Response.json(results);
2558
+ }
2559
+ }
2560
+ }
2561
+ });
2562
+ const ReplayRequestSchema = object({
2563
+ modifiedBody: string()
2564
+ });
2565
+ function getHostFromUrl(urlStr) {
2566
+ try {
2567
+ const url = new URL(urlStr);
2568
+ return url.host;
2569
+ } catch {
2570
+ return "api.anthropic.com";
2571
+ }
2572
+ }
2573
+ function buildUpstreamUrl(upstreamBase, apiPath) {
2574
+ return upstreamBase + apiPath;
2575
+ }
2576
+ function selectUpstreamBase(isChatCompletions, matchedProviderConfig) {
2577
+ let upstreamBase;
2578
+ if (matchedProviderConfig) {
2579
+ if (isChatCompletions && matchedProviderConfig.openaiBaseUrl !== void 0 && matchedProviderConfig.openaiBaseUrl !== "") {
2580
+ upstreamBase = matchedProviderConfig.openaiBaseUrl;
2581
+ } else if (!isChatCompletions && matchedProviderConfig.anthropicBaseUrl !== void 0 && matchedProviderConfig.anthropicBaseUrl !== "") {
2582
+ upstreamBase = matchedProviderConfig.anthropicBaseUrl;
2583
+ } else if (matchedProviderConfig.baseUrl !== void 0 && matchedProviderConfig.baseUrl !== "") {
2584
+ upstreamBase = matchedProviderConfig.baseUrl;
2585
+ } else {
2586
+ upstreamBase = matchedProviderConfig.format === "openai" ? DEFAULT_OPENAI_UPSTREAM$1 : DEFAULT_UPSTREAM$1;
2587
+ }
2588
+ } else {
2589
+ upstreamBase = isChatCompletions ? DEFAULT_OPENAI_UPSTREAM$1 : DEFAULT_UPSTREAM$1;
2590
+ }
2591
+ return upstreamBase;
2592
+ }
2593
+ function injectAuthHeaders(upstreamHeaders, matchedProviderConfig) {
2594
+ if (!matchedProviderConfig) return;
2595
+ const apiKey = matchedProviderConfig.apiKey.replace(/^Bearer\s+/i, "").trim();
2596
+ if (matchedProviderConfig.authHeader === AUTH_HEADER_X_API_KEY) {
2597
+ upstreamHeaders.set(HEADER_X_API_KEY, apiKey);
2598
+ upstreamHeaders.delete(HEADER_AUTHORIZATION);
2599
+ } else {
2600
+ upstreamHeaders.set(HEADER_AUTHORIZATION, `Bearer ${apiKey}`);
2601
+ }
2602
+ }
2603
+ const Route$1 = createFileRoute("/api/logs/$id/replay")({
2604
+ server: {
2605
+ handlers: {
2606
+ POST: async ({ params, request }) => {
2607
+ const id = Number(params.id);
2608
+ if (isNaN(id)) {
2609
+ return Response.json({ error: "Invalid log ID" }, { status: 400 });
2610
+ }
2611
+ let replayRequest;
2612
+ try {
2613
+ const raw = await request.json();
2614
+ const parsed = ReplayRequestSchema.safeParse(raw);
2615
+ if (!parsed.success) {
2616
+ return Response.json({ error: "Invalid request body" }, { status: 400 });
2617
+ }
2618
+ replayRequest = parsed.data;
2619
+ } catch {
2620
+ return Response.json({ error: "Invalid JSON body" }, { status: 400 });
2621
+ }
2622
+ const { modifiedBody } = replayRequest;
2623
+ const log = await getLogById(id);
2624
+ if (log === null) {
2625
+ return Response.json({ error: "Log not found" }, { status: 404 });
2626
+ }
2627
+ const model = extractModelFromBody(modifiedBody);
2628
+ if (model === null) {
2629
+ return Response.json(
2630
+ { error: "Could not extract model from request body" },
2631
+ { status: 400 }
2632
+ );
2633
+ }
2634
+ const matchedProviderConfig = findProviderByModel(model);
2635
+ const provider = registry.findProvider(model);
2636
+ if (provider === null) {
2637
+ return Response.json(
2638
+ { error: "No provider found for model" },
2639
+ { status: STATUS_FORBIDDEN }
2640
+ );
2641
+ }
2642
+ const apiPath = log.path;
2643
+ const isChatCompletions = apiPath === PATH_V1_CHAT_COMPLETIONS || apiPath === PATH_CHAT_COMPLETIONS;
2644
+ const normalizedPath = isChatCompletions && !apiPath.startsWith("/v1/") ? "/v1" + apiPath : apiPath;
2645
+ const formatHandler = formatForPath(apiPath);
2646
+ if (formatHandler === null) {
2647
+ return Response.json({ error: "Unsupported path" }, { status: STATUS_FORBIDDEN });
2648
+ }
2649
+ const upstreamBase = selectUpstreamBase(isChatCompletions, matchedProviderConfig);
2650
+ const upstreamUrl = buildUpstreamUrl(upstreamBase, normalizedPath);
2651
+ const upstreamHost = getHostFromUrl(upstreamBase);
2652
+ const headers = new Headers();
2653
+ headers.set(HEADER_USER_AGENT, PROXY_IDENTITY);
2654
+ headers.set(HEADER_X_PROXY_IDENTITY, PROXY_IDENTITY);
2655
+ headers.set(HEADER_CONTENT_TYPE, "application/json");
2656
+ for (const name of PRESERVE_HEADERS) {
2657
+ const value = log.rawHeaders?.[name.toLowerCase()];
2658
+ if (value !== void 0) {
2659
+ headers.set(name, value);
2660
+ }
2661
+ }
2662
+ headers.set(HEADER_HOST, upstreamHost);
2663
+ injectAuthHeaders(headers, matchedProviderConfig);
2664
+ const startTime = Date.now();
2665
+ let upstreamRes;
2666
+ try {
2667
+ upstreamRes = await fetch(upstreamUrl, {
2668
+ method: "POST",
2669
+ headers,
2670
+ body: modifiedBody
2671
+ });
2672
+ } catch (err) {
2673
+ return Response.json({
2674
+ success: false,
2675
+ error: String(err)
2676
+ });
2677
+ }
2678
+ const elapsedMs = Date.now() - startTime;
2679
+ const isStream = upstreamRes.headers.get(HEADER_CONTENT_TYPE)?.includes("text/event-stream") ?? false;
2680
+ if (isStream) {
2681
+ const chunks = [];
2682
+ const decoder = new TextDecoder();
2683
+ const reader = upstreamRes.body?.getReader();
2684
+ if (reader) {
2685
+ try {
2686
+ while (true) {
2687
+ const { done, value } = await reader.read();
2688
+ if (done) break;
2689
+ chunks.push(decoder.decode(value, { stream: true }));
2690
+ }
2691
+ } catch {
2692
+ }
2693
+ }
2694
+ const fullResponse = chunks.join("");
2695
+ const mockLog = { ...log };
2696
+ const responseText = formatHandler.extractStream(fullResponse, mockLog, model, false);
2697
+ const tokens = formatHandler.extractTokens(responseText);
2698
+ return Response.json({
2699
+ success: true,
2700
+ responseStatus: upstreamRes.status,
2701
+ responseText,
2702
+ inputTokens: tokens.inputTokens ?? void 0,
2703
+ outputTokens: tokens.outputTokens ?? void 0,
2704
+ elapsedMs,
2705
+ streaming: true
2706
+ });
2707
+ } else {
2708
+ const responseText = await upstreamRes.text();
2709
+ const tokens = formatHandler.extractTokens(responseText);
2710
+ return Response.json({
2711
+ success: true,
2712
+ responseStatus: upstreamRes.status,
2713
+ responseText,
2714
+ inputTokens: tokens.inputTokens ?? void 0,
2715
+ outputTokens: tokens.outputTokens ?? void 0,
2716
+ elapsedMs,
2717
+ streaming: false
2718
+ });
2719
+ }
2720
+ }
2721
+ }
2722
+ }
2723
+ });
2724
+ const Route = createFileRoute("/api/logs/$id/chunks")({
2725
+ server: {
2726
+ handlers: {
2727
+ GET: async ({ params }) => {
2728
+ const id = Number(params.id);
2729
+ if (isNaN(id)) {
2730
+ return Response.json({ error: "Invalid log ID" }, { status: 400 });
2731
+ }
2732
+ const log = await getLogById(id);
2733
+ if (log === null) {
2734
+ return Response.json({ error: "Log not found" }, { status: 404 });
2735
+ }
2736
+ if (log.streamingChunksPath !== null && log.streamingChunksPath !== void 0) {
2737
+ const chunksData = readChunks(log.streamingChunksPath);
2738
+ if (chunksData !== null) {
2739
+ return Response.json(chunksData);
2740
+ }
2741
+ }
2742
+ if (log.streamingChunks !== void 0 && log.streamingChunks.chunks.length > 0) {
2743
+ return Response.json(log.streamingChunks);
2744
+ }
2745
+ return Response.json({ error: "Chunks not found" }, { status: 404 });
2746
+ }
2747
+ }
2748
+ }
2749
+ });
2750
+ const IndexRoute = Route$d.update({
2751
+ id: "/",
2752
+ path: "/",
2753
+ getParentRoute: () => Route$e
2754
+ });
2755
+ const ProxySplatRoute = Route$c.update({
2756
+ id: "/proxy/$",
2757
+ path: "/proxy/$",
2758
+ getParentRoute: () => Route$e
2759
+ });
2760
+ const ApiSessionsRoute = Route$b.update({
2761
+ id: "/api/sessions",
2762
+ path: "/api/sessions",
2763
+ getParentRoute: () => Route$e
2764
+ });
2765
+ const ApiProvidersRoute = Route$a.update({
2766
+ id: "/api/providers",
2767
+ path: "/api/providers",
2768
+ getParentRoute: () => Route$e
2769
+ });
2770
+ const ApiModelsRoute = Route$9.update({
2771
+ id: "/api/models",
2772
+ path: "/api/models",
2773
+ getParentRoute: () => Route$e
2774
+ });
2775
+ const ApiLogsRoute = Route$8.update({
2776
+ id: "/api/logs",
2777
+ path: "/api/logs",
2778
+ getParentRoute: () => Route$e
2779
+ });
2780
+ const ApiHealthRoute = Route$7.update({
2781
+ id: "/api/health",
2782
+ path: "/api/health",
2783
+ getParentRoute: () => Route$e
2784
+ });
2785
+ const ApiProvidersProviderIdRoute = Route$6.update({
2786
+ id: "/$providerId",
2787
+ path: "/$providerId",
2788
+ getParentRoute: () => ApiProvidersRoute
2789
+ });
2790
+ const ApiLogsStreamRoute = Route$5.update({
2791
+ id: "/stream",
2792
+ path: "/stream",
2793
+ getParentRoute: () => ApiLogsRoute
2794
+ });
2795
+ const ApiLogsIdRoute = Route$4.update({
2796
+ id: "/$id",
2797
+ path: "/$id",
2798
+ getParentRoute: () => ApiLogsRoute
2799
+ });
2800
+ const ApiConfigPathsRoute = Route$3.update({
2801
+ id: "/api/config/paths",
2802
+ path: "/api/config/paths",
2803
+ getParentRoute: () => Route$e
2804
+ });
2805
+ const ApiProvidersProviderIdTestRoute = Route$2.update({
2806
+ id: "/test",
2807
+ path: "/test",
2808
+ getParentRoute: () => ApiProvidersProviderIdRoute
2809
+ });
2810
+ const ApiLogsIdReplayRoute = Route$1.update({
2811
+ id: "/replay",
2812
+ path: "/replay",
2813
+ getParentRoute: () => ApiLogsIdRoute
2814
+ });
2815
+ const ApiLogsIdChunksRoute = Route.update({
2816
+ id: "/chunks",
2817
+ path: "/chunks",
2818
+ getParentRoute: () => ApiLogsIdRoute
2819
+ });
2820
+ const ApiLogsIdRouteChildren = {
2821
+ ApiLogsIdChunksRoute,
2822
+ ApiLogsIdReplayRoute
2823
+ };
2824
+ const ApiLogsIdRouteWithChildren = ApiLogsIdRoute._addFileChildren(
2825
+ ApiLogsIdRouteChildren
2826
+ );
2827
+ const ApiLogsRouteChildren = {
2828
+ ApiLogsIdRoute: ApiLogsIdRouteWithChildren,
2829
+ ApiLogsStreamRoute
2830
+ };
2831
+ const ApiLogsRouteWithChildren = ApiLogsRoute._addFileChildren(ApiLogsRouteChildren);
2832
+ const ApiProvidersProviderIdRouteChildren = {
2833
+ ApiProvidersProviderIdTestRoute
2834
+ };
2835
+ const ApiProvidersProviderIdRouteWithChildren = ApiProvidersProviderIdRoute._addFileChildren(
2836
+ ApiProvidersProviderIdRouteChildren
2837
+ );
2838
+ const ApiProvidersRouteChildren = {
2839
+ ApiProvidersProviderIdRoute: ApiProvidersProviderIdRouteWithChildren
2840
+ };
2841
+ const ApiProvidersRouteWithChildren = ApiProvidersRoute._addFileChildren(
2842
+ ApiProvidersRouteChildren
2843
+ );
2844
+ const rootRouteChildren = {
2845
+ IndexRoute,
2846
+ ApiHealthRoute,
2847
+ ApiLogsRoute: ApiLogsRouteWithChildren,
2848
+ ApiModelsRoute,
2849
+ ApiProvidersRoute: ApiProvidersRouteWithChildren,
2850
+ ApiSessionsRoute,
2851
+ ProxySplatRoute,
2852
+ ApiConfigPathsRoute
2853
+ };
2854
+ const routeTree = Route$e._addFileChildren(rootRouteChildren)._addFileTypes();
2855
+ function getRouter() {
2856
+ const router2 = createRouter({
2857
+ routeTree,
2858
+ scrollRestoration: false
2859
+ });
2860
+ return router2;
2861
+ }
2862
+ const router = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2863
+ __proto__: null,
2864
+ getRouter
2865
+ }, Symbol.toStringTag, { value: "Module" }));
2866
+ export {
2867
+ CapturedLogSchema as C,
2868
+ InspectorResponseSchema as I,
2869
+ parseRequest as a,
2870
+ parseOpenAIResponse as p,
2871
+ router as r
2872
+ };