@tonyclaw/agent-inspector 3.0.25 → 3.0.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.output/cli.js +80 -11
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{CompareDrawer-BnvOPwpe.js → CompareDrawer-BVCAAbbu.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-CYOXB81n.js +4234 -0
- package/.output/public/assets/{ReplayDialog-BJgoQWdW.js → ReplayDialog-DMX23O5G.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-CpBZEHnM.js → RequestAnatomy-S17pYDRc.js} +1 -1
- package/.output/public/assets/{ResponseView-CWmyRb2M.js → ResponseView-Cf1XrawD.js} +2 -2
- package/.output/public/assets/{StreamingChunkSequence-BWhC8bwx.js → StreamingChunkSequence-BdOrVlCi.js} +1 -1
- package/.output/public/assets/{_sessionId-Cq-2OqUU.js → _sessionId-BnKGmJrR.js} +1 -1
- package/.output/public/assets/{index-Cf8O0Oos.js → index-9fMLCU4d.js} +1 -1
- package/.output/public/assets/{index-BKi9IRtV.js → index-B1atk19i.js} +3 -3
- package/.output/public/assets/index-CmeF5XXS.css +1 -0
- package/.output/public/assets/{index-DdgTJ4QJ.js → index-gY3I-7-D.js} +1 -1
- package/.output/public/assets/{json-viewer-Bqu_4hmm.js → json-viewer-CpyYpPjx.js} +1 -1
- package/.output/server/_libs/lucide-react.mjs +6 -6
- package/.output/server/_libs/three.mjs +26016 -0
- package/.output/server/{_sessionId-Ba7H3Gjw.mjs → _sessionId-eA_h2_s6.mjs} +9 -6
- package/.output/server/_ssr/{CompareDrawer-aH59Ab6x.mjs → CompareDrawer-K2k9G7r1.mjs} +10 -7
- package/.output/server/_ssr/{ProxyViewerContainer-By-I0tJv.mjs → ProxyViewerContainer-CbyBpCAj.mjs} +804 -363
- package/.output/server/_ssr/{ReplayDialog-BO4Vy0et.mjs → ReplayDialog-DacI2Iv0.mjs} +10 -7
- package/.output/server/_ssr/{RequestAnatomy-FtOr7vSS.mjs → RequestAnatomy-gh-pAdkO.mjs} +9 -6
- package/.output/server/_ssr/{ResponseView-cvw6ajue.mjs → ResponseView-BRJKN50-.mjs} +10 -7
- package/.output/server/_ssr/{StreamingChunkSequence-Oc8S2kD6.mjs → StreamingChunkSequence-BQLVEuWY.mjs} +9 -6
- package/.output/server/_ssr/{index-BDRlQheR.mjs → index-DuSWrjbH.mjs} +9 -6
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-Cd6trhQr.mjs → json-viewer-DNs1ZjKI.mjs} +9 -6
- package/.output/server/_ssr/{router-bofP-Ekl.mjs → router-Dgx6-ot3.mjs} +713 -218
- package/.output/server/{_tanstack-start-manifest_v-BPuf86qB.mjs → _tanstack-start-manifest_v-CoGjnkQD.mjs} +1 -1
- package/.output/server/index.mjs +65 -65
- package/README.md +8 -0
- package/package.json +6 -5
- package/src/cli.ts +43 -9
- package/src/components/inspector-pet/InspectorPet.tsx +66 -51
- package/src/components/inspector-pet/InspectorPetCrab3D.tsx +455 -0
- package/src/components/inspector-pet/inspectorPetLogic.ts +28 -43
- package/src/components/providers/ProviderCard.tsx +3 -36
- package/src/components/providers/ProviderForm.tsx +251 -175
- package/src/components/providers/ProvidersPanel.tsx +18 -8
- package/src/components/providers/SettingsDialog.tsx +3 -3
- package/src/components/providers/providerFormCapabilities.ts +36 -0
- package/src/components/proxy-viewer/ProviderLogoStack.tsx +0 -3
- package/src/contracts/anthropic.ts +0 -5
- package/src/contracts/index.ts +0 -4
- package/src/contracts/log.ts +0 -1
- package/src/contracts/openai.ts +0 -3
- package/src/knowledge/openclawClient.ts +22 -9
- package/src/knowledge/openclawGatewayClient.ts +20 -13
- package/src/lib/alertContract.ts +0 -3
- package/src/lib/ecosystemContract.ts +0 -3
- package/src/lib/export-logs.ts +1 -4
- package/src/lib/safeFetch.ts +100 -0
- package/src/lib/ssrfGuard.ts +133 -0
- package/src/lib/stopReason.ts +1 -4
- package/src/lib/unknown.ts +27 -0
- package/src/lib/useProviders.ts +1 -1
- package/src/mcp/mode.ts +3 -9
- package/src/mcp/server.ts +5 -14
- package/src/mcp/toolHandlers.ts +45 -54
- package/src/proxy/constants.ts +17 -0
- package/src/proxy/formats/anthropic/handler.ts +5 -1
- package/src/proxy/formats/anthropic/schemas.ts +1 -7
- package/src/proxy/formats/index.ts +1 -1
- package/src/proxy/formats/openai/schemas.ts +0 -3
- package/src/proxy/formats/registry.ts +0 -3
- package/src/proxy/handler.ts +91 -12
- package/src/proxy/identityProxy.ts +74 -1
- package/src/proxy/logImporter.ts +82 -12
- package/src/proxy/logIndex.ts +1 -4
- package/src/proxy/providerSecretStore.ts +181 -0
- package/src/proxy/providers.ts +84 -11
- package/src/proxy/schemas.ts +1 -8
- package/src/proxy/sqliteLogIndex.ts +0 -16
- package/src/proxy/store.ts +1 -5
- package/src/proxy/toolSchemaWarnings.ts +1 -9
- package/src/routes/api/logs.$id.replay.ts +17 -5
- package/src/routes/api/providers.$providerId.model-metadata.ts +11 -1
- package/src/routes/api/providers.$providerId.ts +18 -5
- package/src/routes/api/providers.ts +18 -7
- package/styles/globals.css +0 -61
- package/.output/public/assets/ProxyViewerContainer-DcqFqo1m.js +0 -127
- package/.output/public/assets/index-BMYuDL5X.css +0 -1
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { type JSX, useState, useEffect, useRef } from "react";
|
|
2
2
|
import { Button } from "../ui/button";
|
|
3
3
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select";
|
|
4
|
-
import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "../ui/tooltip";
|
|
5
4
|
import { Eye, EyeOff, Copy, Check, ChevronDown } from "lucide-react";
|
|
6
5
|
import type { ProviderConfig, ProviderModelMetadata } from "../../lib/providerContract";
|
|
7
6
|
import { copyTextToClipboard } from "../../lib/clipboard";
|
|
8
|
-
import { maskApiKey } from "../../lib/mask";
|
|
9
7
|
import {
|
|
10
8
|
PATH_V1_CHAT_COMPLETIONS,
|
|
11
9
|
PATH_V1_MESSAGES,
|
|
@@ -14,6 +12,7 @@ import {
|
|
|
14
12
|
type UpstreamUrlPreview,
|
|
15
13
|
} from "../../lib/upstreamUrl";
|
|
16
14
|
import { formatContextWindowInput, parseContextWindowTokensInput } from "../../lib/utils";
|
|
15
|
+
import { deriveProviderCapabilities, endpointForCapability } from "./providerFormCapabilities";
|
|
17
16
|
|
|
18
17
|
const ZHIPU_PROVIDER_KEYWORDS = ["zhipu", "zhipuai", "bigmodel", "z.ai", "zai", "glm"];
|
|
19
18
|
const ZHIPU_CODING_PROVIDER_KEYWORDS = [
|
|
@@ -325,8 +324,9 @@ function EndpointUrlPreview({
|
|
|
325
324
|
}
|
|
326
325
|
|
|
327
326
|
export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps): JSX.Element {
|
|
327
|
+
const initialCapabilities = deriveProviderCapabilities(provider);
|
|
328
328
|
const [name, setName] = useState(provider?.name ?? "");
|
|
329
|
-
const [apiKey, setApiKey] = useState(
|
|
329
|
+
const [apiKey, setApiKey] = useState("");
|
|
330
330
|
const [showApiKey, setShowApiKey] = useState(false);
|
|
331
331
|
const [copied, setCopied] = useState(false);
|
|
332
332
|
const initialModels = provider?.models;
|
|
@@ -339,7 +339,12 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
339
339
|
initialModels !== undefined && initialModels.length > 0 ? initialModels : [""],
|
|
340
340
|
),
|
|
341
341
|
);
|
|
342
|
-
const [
|
|
342
|
+
const [supportsAnthropic, setSupportsAnthropic] = useState(initialCapabilities.anthropic);
|
|
343
|
+
const [supportsOpenai, setSupportsOpenai] = useState(initialCapabilities.openai);
|
|
344
|
+
const [supportsOpenaiChat, setSupportsOpenaiChat] = useState(initialCapabilities.openaiChat);
|
|
345
|
+
const [supportsOpenaiResponses, setSupportsOpenaiResponses] = useState(
|
|
346
|
+
initialCapabilities.openaiResponses,
|
|
347
|
+
);
|
|
343
348
|
const [anthropicBaseUrl, setAnthropicBaseUrl] = useState(
|
|
344
349
|
stripEndpointSuffix(provider?.anthropicBaseUrl, ANTHROPIC_MESSAGES_ENDPOINT),
|
|
345
350
|
);
|
|
@@ -405,10 +410,16 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
405
410
|
useEffect(() => {
|
|
406
411
|
if (provider) {
|
|
407
412
|
setName(provider.name);
|
|
408
|
-
setApiKey(
|
|
413
|
+
setApiKey("");
|
|
414
|
+
setShowApiKey(false);
|
|
409
415
|
const nextModels = (provider.models?.length ?? 0) > 0 ? provider.models : [""];
|
|
410
416
|
setModels(nextModels);
|
|
411
417
|
setModelMetadataDrafts(createMetadataDrafts(provider, nextModels));
|
|
418
|
+
const nextCapabilities = deriveProviderCapabilities(provider);
|
|
419
|
+
setSupportsAnthropic(nextCapabilities.anthropic);
|
|
420
|
+
setSupportsOpenai(nextCapabilities.openai);
|
|
421
|
+
setSupportsOpenaiChat(nextCapabilities.openaiChat);
|
|
422
|
+
setSupportsOpenaiResponses(nextCapabilities.openaiResponses);
|
|
412
423
|
setAnthropicBaseUrl(
|
|
413
424
|
stripEndpointSuffix(provider.anthropicBaseUrl, ANTHROPIC_MESSAGES_ENDPOINT),
|
|
414
425
|
);
|
|
@@ -471,20 +482,38 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
471
482
|
if (!name.trim()) {
|
|
472
483
|
newErrors.name = "Name is required";
|
|
473
484
|
}
|
|
474
|
-
if (!apiKey.trim()) {
|
|
485
|
+
if (provider === undefined && !apiKey.trim()) {
|
|
475
486
|
newErrors.apiKey = "API key is required";
|
|
476
487
|
}
|
|
477
488
|
if (models.length === 0 || models.every((m) => !m.trim())) {
|
|
478
489
|
newErrors.models = "At least one model is required";
|
|
479
490
|
}
|
|
480
|
-
if (
|
|
481
|
-
newErrors.
|
|
491
|
+
if (!supportsAnthropic && !supportsOpenai) {
|
|
492
|
+
newErrors.capabilities = "Select at least one API capability";
|
|
493
|
+
}
|
|
494
|
+
if (supportsOpenai && !supportsOpenaiChat && !supportsOpenaiResponses) {
|
|
495
|
+
newErrors.openaiCapabilities = "Select Chat Completions, Responses, or both";
|
|
496
|
+
}
|
|
497
|
+
if (supportsAnthropic) {
|
|
498
|
+
if (anthropicBaseUrl.trim() === "") {
|
|
499
|
+
newErrors.anthropicBaseUrl = "Anthropic base URL is required";
|
|
500
|
+
} else if (!isValidUrl(anthropicBaseUrl.trim())) {
|
|
501
|
+
newErrors.anthropicBaseUrl = "Invalid URL format";
|
|
502
|
+
}
|
|
482
503
|
}
|
|
483
|
-
if (
|
|
484
|
-
|
|
504
|
+
if (supportsOpenai && supportsOpenaiChat) {
|
|
505
|
+
if (openaiBaseUrl.trim() === "") {
|
|
506
|
+
newErrors.openaiBaseUrl = "OpenAI Chat base URL is required";
|
|
507
|
+
} else if (!isValidUrl(openaiBaseUrl.trim())) {
|
|
508
|
+
newErrors.openaiBaseUrl = "Invalid URL format";
|
|
509
|
+
}
|
|
485
510
|
}
|
|
486
|
-
if (
|
|
487
|
-
|
|
511
|
+
if (supportsOpenai && supportsOpenaiResponses) {
|
|
512
|
+
if (openaiResponsesBaseUrl.trim() === "") {
|
|
513
|
+
newErrors.openaiResponsesBaseUrl = "OpenAI Responses base URL is required";
|
|
514
|
+
} else if (!isValidUrl(openaiResponsesBaseUrl.trim())) {
|
|
515
|
+
newErrors.openaiResponsesBaseUrl = "Invalid URL format";
|
|
516
|
+
}
|
|
488
517
|
}
|
|
489
518
|
if (modelMetadataUrl.trim() && !isValidUrl(modelMetadataUrl.trim())) {
|
|
490
519
|
newErrors.modelMetadataUrl = "Invalid URL format";
|
|
@@ -504,9 +533,6 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
504
533
|
"Output limit should not exceed context window";
|
|
505
534
|
}
|
|
506
535
|
});
|
|
507
|
-
if (!anthropicBaseUrl.trim() && !openaiBaseUrl.trim() && !openaiResponsesBaseUrl.trim()) {
|
|
508
|
-
newErrors.format = "At least one format URL (Anthropic or OpenAI) is required";
|
|
509
|
-
}
|
|
510
536
|
setErrors(newErrors);
|
|
511
537
|
return Object.keys(newErrors).length === 0;
|
|
512
538
|
}
|
|
@@ -631,14 +657,27 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
631
657
|
e.preventDefault();
|
|
632
658
|
if (!validate()) return;
|
|
633
659
|
setIsSubmitting(true);
|
|
660
|
+
const clearDisabledEndpoints = provider !== undefined;
|
|
634
661
|
try {
|
|
635
662
|
onSubmit({
|
|
636
663
|
name: name.trim(),
|
|
637
664
|
apiKey: apiKey.trim(),
|
|
638
665
|
models: models.map((m) => m.trim()).filter((m) => m !== ""),
|
|
639
|
-
anthropicBaseUrl:
|
|
640
|
-
|
|
641
|
-
|
|
666
|
+
anthropicBaseUrl: endpointForCapability(
|
|
667
|
+
supportsAnthropic,
|
|
668
|
+
anthropicBaseUrl,
|
|
669
|
+
clearDisabledEndpoints,
|
|
670
|
+
),
|
|
671
|
+
openaiBaseUrl: endpointForCapability(
|
|
672
|
+
supportsOpenai && supportsOpenaiChat,
|
|
673
|
+
openaiBaseUrl,
|
|
674
|
+
clearDisabledEndpoints,
|
|
675
|
+
),
|
|
676
|
+
openaiResponsesBaseUrl: endpointForCapability(
|
|
677
|
+
supportsOpenai && supportsOpenaiResponses,
|
|
678
|
+
openaiResponsesBaseUrl,
|
|
679
|
+
clearDisabledEndpoints,
|
|
680
|
+
),
|
|
642
681
|
apiDocsUrl: apiDocsUrl.trim() || undefined,
|
|
643
682
|
modelMetadataUrl: modelMetadataUrl.trim() || undefined,
|
|
644
683
|
modelMetadata: buildModelMetadata(),
|
|
@@ -666,6 +705,191 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
666
705
|
{errors.name !== undefined && <p className="text-xs text-destructive">{errors.name}</p>}
|
|
667
706
|
</div>
|
|
668
707
|
|
|
708
|
+
<fieldset className="space-y-3">
|
|
709
|
+
<legend className="text-sm font-medium">
|
|
710
|
+
Capabilities <span className="text-destructive">*</span>
|
|
711
|
+
</legend>
|
|
712
|
+
<div className="divide-y divide-border overflow-hidden rounded-md border border-border">
|
|
713
|
+
<div className={supportsOpenai ? "bg-primary/[0.04] px-3 py-3" : "px-3 py-3"}>
|
|
714
|
+
<label
|
|
715
|
+
htmlFor="provider-capability-openai"
|
|
716
|
+
className="flex cursor-pointer items-start gap-3"
|
|
717
|
+
>
|
|
718
|
+
<input
|
|
719
|
+
id="provider-capability-openai"
|
|
720
|
+
type="checkbox"
|
|
721
|
+
checked={supportsOpenai}
|
|
722
|
+
onChange={(event) => setSupportsOpenai(event.target.checked)}
|
|
723
|
+
className="mt-0.5 size-4 shrink-0 accent-primary"
|
|
724
|
+
/>
|
|
725
|
+
<span className="min-w-0">
|
|
726
|
+
<span className="block text-sm font-medium">OpenAI</span>
|
|
727
|
+
<span className="block text-xs text-muted-foreground">
|
|
728
|
+
Chat Completions and Responses
|
|
729
|
+
</span>
|
|
730
|
+
</span>
|
|
731
|
+
</label>
|
|
732
|
+
{supportsOpenai && (
|
|
733
|
+
<div className="ml-7 mt-3 flex flex-wrap gap-x-5 gap-y-2 border-t border-border/70 pt-3">
|
|
734
|
+
<label
|
|
735
|
+
htmlFor="provider-capability-openai-chat"
|
|
736
|
+
className="flex cursor-pointer items-center gap-2 text-sm"
|
|
737
|
+
>
|
|
738
|
+
<input
|
|
739
|
+
id="provider-capability-openai-chat"
|
|
740
|
+
type="checkbox"
|
|
741
|
+
checked={supportsOpenaiChat}
|
|
742
|
+
onChange={(event) => setSupportsOpenaiChat(event.target.checked)}
|
|
743
|
+
className="size-4 shrink-0 accent-primary"
|
|
744
|
+
/>
|
|
745
|
+
Chat Completions
|
|
746
|
+
</label>
|
|
747
|
+
<label
|
|
748
|
+
htmlFor="provider-capability-openai-responses"
|
|
749
|
+
className="flex cursor-pointer items-center gap-2 text-sm"
|
|
750
|
+
>
|
|
751
|
+
<input
|
|
752
|
+
id="provider-capability-openai-responses"
|
|
753
|
+
type="checkbox"
|
|
754
|
+
checked={supportsOpenaiResponses}
|
|
755
|
+
onChange={(event) => setSupportsOpenaiResponses(event.target.checked)}
|
|
756
|
+
className="size-4 shrink-0 accent-primary"
|
|
757
|
+
/>
|
|
758
|
+
Responses
|
|
759
|
+
</label>
|
|
760
|
+
</div>
|
|
761
|
+
)}
|
|
762
|
+
{errors.openaiCapabilities !== undefined && supportsOpenai && (
|
|
763
|
+
<p className="ml-7 mt-2 text-xs text-destructive">{errors.openaiCapabilities}</p>
|
|
764
|
+
)}
|
|
765
|
+
</div>
|
|
766
|
+
<label
|
|
767
|
+
htmlFor="provider-capability-anthropic"
|
|
768
|
+
className={`flex cursor-pointer items-start gap-3 px-3 py-3 ${
|
|
769
|
+
supportsAnthropic ? "bg-primary/[0.04]" : ""
|
|
770
|
+
}`}
|
|
771
|
+
>
|
|
772
|
+
<input
|
|
773
|
+
id="provider-capability-anthropic"
|
|
774
|
+
type="checkbox"
|
|
775
|
+
checked={supportsAnthropic}
|
|
776
|
+
onChange={(event) => setSupportsAnthropic(event.target.checked)}
|
|
777
|
+
className="mt-0.5 size-4 shrink-0 accent-primary"
|
|
778
|
+
/>
|
|
779
|
+
<span className="min-w-0">
|
|
780
|
+
<span className="block text-sm font-medium">Anthropic</span>
|
|
781
|
+
<span className="block text-xs text-muted-foreground">Messages</span>
|
|
782
|
+
</span>
|
|
783
|
+
</label>
|
|
784
|
+
</div>
|
|
785
|
+
{errors.capabilities !== undefined && (
|
|
786
|
+
<p className="text-xs text-destructive">{errors.capabilities}</p>
|
|
787
|
+
)}
|
|
788
|
+
</fieldset>
|
|
789
|
+
|
|
790
|
+
{(supportsAnthropic ||
|
|
791
|
+
(supportsOpenai && (supportsOpenaiChat || supportsOpenaiResponses))) && (
|
|
792
|
+
<section className="space-y-5 border-l border-border pl-4" aria-label="API endpoints">
|
|
793
|
+
<h3 className="text-sm font-medium">Endpoint Configuration</h3>
|
|
794
|
+
|
|
795
|
+
{supportsAnthropic && (
|
|
796
|
+
<div className="space-y-2">
|
|
797
|
+
<label htmlFor="provider-anthropic-base-url" className="text-sm font-medium">
|
|
798
|
+
Anthropic Messages Base URL <span className="text-destructive">*</span>
|
|
799
|
+
</label>
|
|
800
|
+
<div className="border border-input bg-background hover:bg-accent hover:text-accent-foreground flex min-w-0 overflow-hidden rounded-md focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]">
|
|
801
|
+
<input
|
|
802
|
+
id="provider-anthropic-base-url"
|
|
803
|
+
type="text"
|
|
804
|
+
value={anthropicBaseUrl}
|
|
805
|
+
onChange={(event) => {
|
|
806
|
+
setManualAnthropicUrlOverride(true);
|
|
807
|
+
setAnthropicBaseUrl(event.target.value);
|
|
808
|
+
}}
|
|
809
|
+
placeholder="https://api.anthropic.com"
|
|
810
|
+
className="min-w-0 flex-1 bg-transparent px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
|
811
|
+
/>
|
|
812
|
+
<span className="shrink-0 select-none border-l border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-xs text-muted-foreground">
|
|
813
|
+
{ANTHROPIC_MESSAGES_ENDPOINT}
|
|
814
|
+
</span>
|
|
815
|
+
</div>
|
|
816
|
+
{errors.anthropicBaseUrl !== undefined && (
|
|
817
|
+
<p className="text-xs text-destructive">{errors.anthropicBaseUrl}</p>
|
|
818
|
+
)}
|
|
819
|
+
<EndpointUrlPreview
|
|
820
|
+
baseUrl={anthropicBaseUrl}
|
|
821
|
+
endpoint={ANTHROPIC_MESSAGES_ENDPOINT}
|
|
822
|
+
providerName={name}
|
|
823
|
+
/>
|
|
824
|
+
</div>
|
|
825
|
+
)}
|
|
826
|
+
|
|
827
|
+
{supportsOpenai && supportsOpenaiChat && (
|
|
828
|
+
<div className="space-y-2">
|
|
829
|
+
<label htmlFor="provider-openai-base-url" className="text-sm font-medium">
|
|
830
|
+
OpenAI Chat Base URL <span className="text-destructive">*</span>
|
|
831
|
+
</label>
|
|
832
|
+
<div className="border border-input bg-background hover:bg-accent hover:text-accent-foreground flex min-w-0 overflow-hidden rounded-md focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]">
|
|
833
|
+
<input
|
|
834
|
+
id="provider-openai-base-url"
|
|
835
|
+
type="text"
|
|
836
|
+
value={openaiBaseUrl}
|
|
837
|
+
onChange={(event) => {
|
|
838
|
+
setManualOpenaiUrlOverride(true);
|
|
839
|
+
setOpenaiBaseUrl(event.target.value);
|
|
840
|
+
}}
|
|
841
|
+
placeholder="https://api.openai.com"
|
|
842
|
+
className="min-w-0 flex-1 bg-transparent px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
|
843
|
+
/>
|
|
844
|
+
<span className="shrink-0 select-none border-l border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-xs text-muted-foreground">
|
|
845
|
+
{OPENAI_CHAT_COMPLETIONS_ENDPOINT}
|
|
846
|
+
</span>
|
|
847
|
+
</div>
|
|
848
|
+
{errors.openaiBaseUrl !== undefined && (
|
|
849
|
+
<p className="text-xs text-destructive">{errors.openaiBaseUrl}</p>
|
|
850
|
+
)}
|
|
851
|
+
<EndpointUrlPreview
|
|
852
|
+
baseUrl={openaiBaseUrl}
|
|
853
|
+
endpoint={OPENAI_CHAT_COMPLETIONS_ENDPOINT}
|
|
854
|
+
providerName={name}
|
|
855
|
+
/>
|
|
856
|
+
</div>
|
|
857
|
+
)}
|
|
858
|
+
|
|
859
|
+
{supportsOpenai && supportsOpenaiResponses && (
|
|
860
|
+
<div className="space-y-2">
|
|
861
|
+
<label htmlFor="provider-openai-responses-base-url" className="text-sm font-medium">
|
|
862
|
+
OpenAI Responses Base URL <span className="text-destructive">*</span>
|
|
863
|
+
</label>
|
|
864
|
+
<div className="border border-input bg-background hover:bg-accent hover:text-accent-foreground flex min-w-0 overflow-hidden rounded-md focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]">
|
|
865
|
+
<input
|
|
866
|
+
id="provider-openai-responses-base-url"
|
|
867
|
+
type="text"
|
|
868
|
+
value={openaiResponsesBaseUrl}
|
|
869
|
+
onChange={(event) => {
|
|
870
|
+
setManualOpenaiResponsesUrlOverride(true);
|
|
871
|
+
setOpenaiResponsesBaseUrl(event.target.value);
|
|
872
|
+
}}
|
|
873
|
+
placeholder="https://api.openai.com"
|
|
874
|
+
className="min-w-0 flex-1 bg-transparent px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
|
875
|
+
/>
|
|
876
|
+
<span className="shrink-0 select-none border-l border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-xs text-muted-foreground">
|
|
877
|
+
{OPENAI_RESPONSES_ENDPOINT}
|
|
878
|
+
</span>
|
|
879
|
+
</div>
|
|
880
|
+
{errors.openaiResponsesBaseUrl !== undefined && (
|
|
881
|
+
<p className="text-xs text-destructive">{errors.openaiResponsesBaseUrl}</p>
|
|
882
|
+
)}
|
|
883
|
+
<EndpointUrlPreview
|
|
884
|
+
baseUrl={openaiResponsesBaseUrl}
|
|
885
|
+
endpoint={OPENAI_RESPONSES_ENDPOINT}
|
|
886
|
+
providerName={name}
|
|
887
|
+
/>
|
|
888
|
+
</div>
|
|
889
|
+
)}
|
|
890
|
+
</section>
|
|
891
|
+
)}
|
|
892
|
+
|
|
669
893
|
<div className="space-y-2">
|
|
670
894
|
<label htmlFor="provider-source" className="text-sm font-medium">
|
|
671
895
|
Token Source
|
|
@@ -726,19 +950,15 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
726
950
|
|
|
727
951
|
<div className="space-y-2">
|
|
728
952
|
<label htmlFor="provider-apikey" className="text-sm font-medium">
|
|
729
|
-
API Key <span className="text-destructive">*</span>
|
|
953
|
+
API Key {provider === undefined && <span className="text-destructive">*</span>}
|
|
730
954
|
</label>
|
|
731
955
|
<div className="flex items-center gap-2">
|
|
732
956
|
<input
|
|
733
957
|
id="provider-apikey"
|
|
734
|
-
type="text"
|
|
735
|
-
value={
|
|
958
|
+
type={showApiKey ? "text" : "password"}
|
|
959
|
+
value={apiKey}
|
|
736
960
|
onChange={(e) => setApiKey(e.target.value)}
|
|
737
|
-
|
|
738
|
-
if (!showApiKey && apiKey.length > 0) setShowApiKey(true);
|
|
739
|
-
}}
|
|
740
|
-
placeholder="sk-ant-..."
|
|
741
|
-
readOnly={!showApiKey && apiKey.length > 0}
|
|
961
|
+
placeholder={provider === undefined ? "sk-ant-..." : "Leave blank to keep current key"}
|
|
742
962
|
className="flex-1 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50"
|
|
743
963
|
/>
|
|
744
964
|
<button
|
|
@@ -758,6 +978,11 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
758
978
|
{copied ? <Check className="size-4 text-cyan-300" /> : <Copy className="size-4" />}
|
|
759
979
|
</button>
|
|
760
980
|
</div>
|
|
981
|
+
{provider !== undefined && apiKey.trim() === "" && (
|
|
982
|
+
<p className="text-xs text-muted-foreground">
|
|
983
|
+
A key is configured. Enter a new value only to rotate it.
|
|
984
|
+
</p>
|
|
985
|
+
)}
|
|
761
986
|
{errors.apiKey !== undefined && <p className="text-xs text-destructive">{errors.apiKey}</p>}
|
|
762
987
|
</div>
|
|
763
988
|
|
|
@@ -924,155 +1149,6 @@ export function ProviderForm({ provider, onSubmit, onCancel }: ProviderFormProps
|
|
|
924
1149
|
{errors.models !== undefined && <p className="text-xs text-destructive">{errors.models}</p>}
|
|
925
1150
|
</div>
|
|
926
1151
|
|
|
927
|
-
<div className="space-y-2">
|
|
928
|
-
<div className="border border-border bg-muted/40 flex gap-1 rounded-[8px] p-1">
|
|
929
|
-
<TooltipProvider>
|
|
930
|
-
<Tooltip>
|
|
931
|
-
<TooltipTrigger asChild>
|
|
932
|
-
<button
|
|
933
|
-
type="button"
|
|
934
|
-
onClick={() => setActiveTab("anthropic")}
|
|
935
|
-
className={`px-3 py-2 text-sm font-medium border-b-2 transition-colors ${
|
|
936
|
-
activeTab === "anthropic"
|
|
937
|
-
? "border-primary text-primary"
|
|
938
|
-
: "border-transparent text-muted-foreground hover:text-foreground"
|
|
939
|
-
}`}
|
|
940
|
-
>
|
|
941
|
-
Anthropic Format
|
|
942
|
-
</button>
|
|
943
|
-
</TooltipTrigger>
|
|
944
|
-
<TooltipContent>Anthropic Messages API format</TooltipContent>
|
|
945
|
-
</Tooltip>
|
|
946
|
-
</TooltipProvider>
|
|
947
|
-
<TooltipProvider>
|
|
948
|
-
<Tooltip>
|
|
949
|
-
<TooltipTrigger asChild>
|
|
950
|
-
<button
|
|
951
|
-
type="button"
|
|
952
|
-
onClick={() => setActiveTab("openai")}
|
|
953
|
-
className={`px-3 py-2 text-sm font-medium border-b-2 transition-colors ${
|
|
954
|
-
activeTab === "openai"
|
|
955
|
-
? "border-primary text-primary"
|
|
956
|
-
: "border-transparent text-muted-foreground hover:text-foreground"
|
|
957
|
-
}`}
|
|
958
|
-
>
|
|
959
|
-
OpenAI Format
|
|
960
|
-
</button>
|
|
961
|
-
</TooltipTrigger>
|
|
962
|
-
<TooltipContent>OpenAI Chat Completions API format</TooltipContent>
|
|
963
|
-
</Tooltip>
|
|
964
|
-
</TooltipProvider>
|
|
965
|
-
</div>
|
|
966
|
-
{errors.format !== undefined && <p className="text-xs text-destructive">{errors.format}</p>}
|
|
967
|
-
</div>
|
|
968
|
-
|
|
969
|
-
{activeTab === "anthropic" && (
|
|
970
|
-
<div className="space-y-2">
|
|
971
|
-
<label htmlFor="provider-anthropic-base-url" className="text-sm font-medium">
|
|
972
|
-
Anthropic Base URL
|
|
973
|
-
</label>
|
|
974
|
-
<div className="border border-input bg-background hover:bg-accent hover:text-accent-foreground flex min-w-0 overflow-hidden rounded-md focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]">
|
|
975
|
-
<input
|
|
976
|
-
id="provider-anthropic-base-url"
|
|
977
|
-
type="text"
|
|
978
|
-
value={anthropicBaseUrl}
|
|
979
|
-
onChange={(e) => {
|
|
980
|
-
setManualAnthropicUrlOverride(true);
|
|
981
|
-
setAnthropicBaseUrl(e.target.value);
|
|
982
|
-
}}
|
|
983
|
-
placeholder="https://api.anthropic.com"
|
|
984
|
-
className="min-w-0 flex-1 bg-transparent px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
|
985
|
-
/>
|
|
986
|
-
<span className="shrink-0 select-none border-l border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-xs text-muted-foreground">
|
|
987
|
-
{ANTHROPIC_MESSAGES_ENDPOINT}
|
|
988
|
-
</span>
|
|
989
|
-
</div>
|
|
990
|
-
{errors.anthropicBaseUrl !== undefined && (
|
|
991
|
-
<p className="text-xs text-destructive">{errors.anthropicBaseUrl}</p>
|
|
992
|
-
)}
|
|
993
|
-
<EndpointUrlPreview
|
|
994
|
-
baseUrl={anthropicBaseUrl}
|
|
995
|
-
endpoint={ANTHROPIC_MESSAGES_ENDPOINT}
|
|
996
|
-
providerName={name}
|
|
997
|
-
/>
|
|
998
|
-
<p className="text-xs text-muted-foreground">
|
|
999
|
-
Anthropic-compatible base URL. Leave empty if this provider does not support Anthropic
|
|
1000
|
-
format.
|
|
1001
|
-
</p>
|
|
1002
|
-
</div>
|
|
1003
|
-
)}
|
|
1004
|
-
|
|
1005
|
-
{activeTab === "openai" && (
|
|
1006
|
-
<div className="space-y-4">
|
|
1007
|
-
<div className="space-y-2">
|
|
1008
|
-
<label htmlFor="provider-openai-base-url" className="text-sm font-medium">
|
|
1009
|
-
OpenAI Chat Base URL
|
|
1010
|
-
</label>
|
|
1011
|
-
<div className="border border-input bg-background hover:bg-accent hover:text-accent-foreground flex min-w-0 overflow-hidden rounded-md focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]">
|
|
1012
|
-
<input
|
|
1013
|
-
id="provider-openai-base-url"
|
|
1014
|
-
type="text"
|
|
1015
|
-
value={openaiBaseUrl}
|
|
1016
|
-
onChange={(e) => {
|
|
1017
|
-
setManualOpenaiUrlOverride(true);
|
|
1018
|
-
setOpenaiBaseUrl(e.target.value);
|
|
1019
|
-
}}
|
|
1020
|
-
placeholder="https://api.openai.com"
|
|
1021
|
-
className="min-w-0 flex-1 bg-transparent px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
|
1022
|
-
/>
|
|
1023
|
-
<span className="shrink-0 select-none border-l border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-xs text-muted-foreground">
|
|
1024
|
-
{OPENAI_CHAT_COMPLETIONS_ENDPOINT}
|
|
1025
|
-
</span>
|
|
1026
|
-
</div>
|
|
1027
|
-
{errors.openaiBaseUrl !== undefined && (
|
|
1028
|
-
<p className="text-xs text-destructive">{errors.openaiBaseUrl}</p>
|
|
1029
|
-
)}
|
|
1030
|
-
<EndpointUrlPreview
|
|
1031
|
-
baseUrl={openaiBaseUrl}
|
|
1032
|
-
endpoint={OPENAI_CHAT_COMPLETIONS_ENDPOINT}
|
|
1033
|
-
providerName={name}
|
|
1034
|
-
/>
|
|
1035
|
-
<p className="text-xs text-muted-foreground">
|
|
1036
|
-
OpenAI Chat Completions-compatible base URL. Leave empty if this provider only
|
|
1037
|
-
supports the Responses API.
|
|
1038
|
-
</p>
|
|
1039
|
-
</div>
|
|
1040
|
-
<div className="space-y-2">
|
|
1041
|
-
<label htmlFor="provider-openai-responses-base-url" className="text-sm font-medium">
|
|
1042
|
-
OpenAI Responses Base URL
|
|
1043
|
-
</label>
|
|
1044
|
-
<div className="border border-input bg-background hover:bg-accent hover:text-accent-foreground flex min-w-0 overflow-hidden rounded-md focus-within:border-ring focus-within:outline-ring focus-within:ring-[3px]">
|
|
1045
|
-
<input
|
|
1046
|
-
id="provider-openai-responses-base-url"
|
|
1047
|
-
type="text"
|
|
1048
|
-
value={openaiResponsesBaseUrl}
|
|
1049
|
-
onChange={(e) => {
|
|
1050
|
-
setManualOpenaiResponsesUrlOverride(true);
|
|
1051
|
-
setOpenaiResponsesBaseUrl(e.target.value);
|
|
1052
|
-
}}
|
|
1053
|
-
placeholder="https://api.openai.com"
|
|
1054
|
-
className="min-w-0 flex-1 bg-transparent px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
|
1055
|
-
/>
|
|
1056
|
-
<span className="shrink-0 select-none border-l border-white/10 bg-white/[0.04] px-3 py-2 font-mono text-xs text-muted-foreground">
|
|
1057
|
-
{OPENAI_RESPONSES_ENDPOINT}
|
|
1058
|
-
</span>
|
|
1059
|
-
</div>
|
|
1060
|
-
{errors.openaiResponsesBaseUrl !== undefined && (
|
|
1061
|
-
<p className="text-xs text-destructive">{errors.openaiResponsesBaseUrl}</p>
|
|
1062
|
-
)}
|
|
1063
|
-
<EndpointUrlPreview
|
|
1064
|
-
baseUrl={openaiResponsesBaseUrl}
|
|
1065
|
-
endpoint={OPENAI_RESPONSES_ENDPOINT}
|
|
1066
|
-
providerName={name}
|
|
1067
|
-
/>
|
|
1068
|
-
<p className="text-xs text-muted-foreground">
|
|
1069
|
-
Optional Responses-compatible base URL. Set only when the provider supports the
|
|
1070
|
-
Responses API.
|
|
1071
|
-
</p>
|
|
1072
|
-
</div>
|
|
1073
|
-
</div>
|
|
1074
|
-
)}
|
|
1075
|
-
|
|
1076
1152
|
<div className="space-y-2">
|
|
1077
1153
|
<label htmlFor="provider-api-docs-url" className="text-sm font-medium">
|
|
1078
1154
|
API Docs URL
|
|
@@ -70,15 +70,25 @@ type ProviderFormData = {
|
|
|
70
70
|
source?: "company" | "personal";
|
|
71
71
|
};
|
|
72
72
|
|
|
73
|
-
function
|
|
73
|
+
function endpointPayloadValue(
|
|
74
|
+
value: string | undefined,
|
|
75
|
+
preserveEmptyEndpoints: boolean,
|
|
76
|
+
): string | undefined {
|
|
77
|
+
if ((value?.length ?? 0) > 0) return value;
|
|
78
|
+
return preserveEmptyEndpoints && value !== undefined ? "" : undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function createProviderPayload(data: ProviderFormData, preserveEmptyEndpoints = false) {
|
|
74
82
|
return {
|
|
75
83
|
name: data.name,
|
|
76
|
-
apiKey: data.apiKey,
|
|
84
|
+
...(data.apiKey.trim() === "" ? {} : { apiKey: data.apiKey }),
|
|
77
85
|
models: data.models,
|
|
78
|
-
anthropicBaseUrl: (data.anthropicBaseUrl
|
|
79
|
-
openaiBaseUrl: (data.openaiBaseUrl
|
|
80
|
-
openaiResponsesBaseUrl:
|
|
81
|
-
|
|
86
|
+
anthropicBaseUrl: endpointPayloadValue(data.anthropicBaseUrl, preserveEmptyEndpoints),
|
|
87
|
+
openaiBaseUrl: endpointPayloadValue(data.openaiBaseUrl, preserveEmptyEndpoints),
|
|
88
|
+
openaiResponsesBaseUrl: endpointPayloadValue(
|
|
89
|
+
data.openaiResponsesBaseUrl,
|
|
90
|
+
preserveEmptyEndpoints,
|
|
91
|
+
),
|
|
82
92
|
apiDocsUrl: (data.apiDocsUrl?.length ?? 0) > 0 ? data.apiDocsUrl : undefined,
|
|
83
93
|
modelMetadataUrl: (data.modelMetadataUrl?.length ?? 0) > 0 ? data.modelMetadataUrl : undefined,
|
|
84
94
|
modelMetadata: data.modelMetadata,
|
|
@@ -408,7 +418,7 @@ export function ProvidersPanel({
|
|
|
408
418
|
{
|
|
409
419
|
method: "PUT",
|
|
410
420
|
headers: { "Content-Type": "application/json" },
|
|
411
|
-
body: JSON.stringify(createProviderPayload(data)),
|
|
421
|
+
body: JSON.stringify(createProviderPayload(data, true)),
|
|
412
422
|
},
|
|
413
423
|
() => "Failed to update provider",
|
|
414
424
|
);
|
|
@@ -420,7 +430,7 @@ export function ProvidersPanel({
|
|
|
420
430
|
refreshProviders();
|
|
421
431
|
// Only run connection test when critical fields (apiKey, model, base URLs) changed
|
|
422
432
|
const criticalFieldsChanged =
|
|
423
|
-
|
|
433
|
+
data.apiKey.trim() !== "" ||
|
|
424
434
|
JSON.stringify(data.models) !== JSON.stringify(editingProvider.models) ||
|
|
425
435
|
(data.anthropicBaseUrl ?? "") !== (editingProvider.anthropicBaseUrl ?? "") ||
|
|
426
436
|
(data.openaiBaseUrl ?? "") !== (editingProvider.openaiBaseUrl ?? "") ||
|
|
@@ -471,9 +471,9 @@ function McpSettingsTab(): JSX.Element {
|
|
|
471
471
|
value: `agent-inspector-mcp doctor --url ${endpoint}`,
|
|
472
472
|
},
|
|
473
473
|
{
|
|
474
|
-
id: "
|
|
475
|
-
label: "
|
|
476
|
-
value: "
|
|
474
|
+
id: "writes",
|
|
475
|
+
label: "Enable write tools",
|
|
476
|
+
value: "AGENT_INSPECTOR_MCP_WRITES=1 agent-inspector",
|
|
477
477
|
},
|
|
478
478
|
{
|
|
479
479
|
id: "capabilities",
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ProviderConfig } from "../../lib/providerContract";
|
|
2
|
+
|
|
3
|
+
export type ProviderCapabilitySelection = {
|
|
4
|
+
anthropic: boolean;
|
|
5
|
+
openai: boolean;
|
|
6
|
+
openaiChat: boolean;
|
|
7
|
+
openaiResponses: boolean;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
function hasEndpoint(value: string | undefined): boolean {
|
|
11
|
+
return value !== undefined && value.trim() !== "";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function deriveProviderCapabilities(
|
|
15
|
+
provider: ProviderConfig | undefined,
|
|
16
|
+
): ProviderCapabilitySelection {
|
|
17
|
+
const anthropic = hasEndpoint(provider?.anthropicBaseUrl);
|
|
18
|
+
const openaiChat = hasEndpoint(provider?.openaiBaseUrl);
|
|
19
|
+
const openaiResponses = hasEndpoint(provider?.openaiResponsesBaseUrl);
|
|
20
|
+
return {
|
|
21
|
+
anthropic,
|
|
22
|
+
openai: openaiChat || openaiResponses,
|
|
23
|
+
openaiChat,
|
|
24
|
+
openaiResponses,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function endpointForCapability(
|
|
29
|
+
enabled: boolean,
|
|
30
|
+
value: string,
|
|
31
|
+
clearWhenDisabled: boolean,
|
|
32
|
+
): string | undefined {
|
|
33
|
+
if (!enabled) return clearWhenDisabled ? "" : undefined;
|
|
34
|
+
const trimmed = value.trim();
|
|
35
|
+
return trimmed === "" ? undefined : trimmed;
|
|
36
|
+
}
|
|
@@ -5,9 +5,6 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "../ui/
|
|
|
5
5
|
|
|
6
6
|
const MAX_VISIBLE_LOGOS = 3;
|
|
7
7
|
|
|
8
|
-
export const ROW_CHEVRON_SLOT_CLASS =
|
|
9
|
-
"flex h-8 w-5 shrink-0 items-center justify-center text-muted-foreground";
|
|
10
|
-
|
|
11
8
|
export type ProviderLogoStackItem = {
|
|
12
9
|
provider: Provider;
|
|
13
10
|
tooltip: string;
|
|
@@ -228,8 +228,3 @@ export const SseEventSchema = z.discriminatedUnion("type", [
|
|
|
228
228
|
|
|
229
229
|
export type AnthropicResponse = z.infer<typeof AnthropicResponseSchema>;
|
|
230
230
|
export type ResponseContentBlockType = z.infer<typeof ResponseContentBlock>;
|
|
231
|
-
|
|
232
|
-
// Backward-compatible aliases (originally named Inspector*)
|
|
233
|
-
export const InspectorRequestSchema = AnthropicRequestSchema;
|
|
234
|
-
export const InspectorResponseSchema = AnthropicResponseSchema;
|
|
235
|
-
export type InspectorResponse = AnthropicResponse;
|
package/src/contracts/index.ts
CHANGED
|
@@ -12,7 +12,6 @@ export type {
|
|
|
12
12
|
CapturedLog,
|
|
13
13
|
LogBodyChunk,
|
|
14
14
|
LogBodyPart,
|
|
15
|
-
RequestFormat,
|
|
16
15
|
StreamingChunk,
|
|
17
16
|
TokenUsage,
|
|
18
17
|
} from "./log";
|
|
@@ -46,9 +45,6 @@ export {
|
|
|
46
45
|
} from "./openai";
|
|
47
46
|
export type {
|
|
48
47
|
OpenAIResponse,
|
|
49
|
-
OpenAIResponsesRequest,
|
|
50
48
|
OpenAIResponsesResponse,
|
|
51
|
-
OpenAIResponsesSSEvent,
|
|
52
|
-
OpenAIResponsesOutputItem,
|
|
53
49
|
OpenAIToolCall,
|
|
54
50
|
} from "./openai";
|