@mariozechner/pi-web-ui 0.5.48 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ChatPanel.d.ts +1 -0
- package/dist/ChatPanel.d.ts.map +1 -1
- package/dist/ChatPanel.js +3 -2
- package/dist/ChatPanel.js.map +1 -1
- package/dist/agent/transports/ProviderTransport.d.ts +1 -1
- package/dist/agent/transports/ProviderTransport.d.ts.map +1 -1
- package/dist/agent/transports/ProviderTransport.js +5 -10
- package/dist/agent/transports/ProviderTransport.js.map +1 -1
- package/dist/app.css +4188 -2
- package/dist/components/AgentInterface.d.ts +1 -0
- package/dist/components/AgentInterface.d.ts.map +1 -1
- package/dist/components/AgentInterface.js +13 -3
- package/dist/components/AgentInterface.js.map +1 -1
- package/dist/components/AttachmentTile.d.ts.map +1 -1
- package/dist/components/AttachmentTile.js +2 -1
- package/dist/components/AttachmentTile.js.map +1 -1
- package/dist/components/ConsoleBlock.d.ts.map +1 -1
- package/dist/components/ConsoleBlock.js +2 -1
- package/dist/components/ConsoleBlock.js.map +1 -1
- package/dist/components/CustomProviderCard.d.ts +17 -0
- package/dist/components/CustomProviderCard.d.ts.map +1 -0
- package/dist/components/CustomProviderCard.js +110 -0
- package/dist/components/CustomProviderCard.js.map +1 -0
- package/dist/components/Input.d.ts +2 -2
- package/dist/components/Input.d.ts.map +1 -1
- package/dist/components/Input.js +2 -1
- package/dist/components/Input.js.map +1 -1
- package/dist/components/MessageEditor.d.ts +1 -3
- package/dist/components/MessageEditor.d.ts.map +1 -1
- package/dist/components/MessageEditor.js +6 -31
- package/dist/components/MessageEditor.js.map +1 -1
- package/dist/components/MessageList.d.ts +1 -0
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +6 -3
- package/dist/components/MessageList.js.map +1 -1
- package/dist/components/Messages.d.ts +2 -0
- package/dist/components/Messages.d.ts.map +1 -1
- package/dist/components/Messages.js +25 -14
- package/dist/components/Messages.js.map +1 -1
- package/dist/components/ProviderKeyInput.d.ts +1 -1
- package/dist/components/ProviderKeyInput.d.ts.map +1 -1
- package/dist/components/ProviderKeyInput.js +22 -36
- package/dist/components/ProviderKeyInput.js.map +1 -1
- package/dist/components/StreamingMessageContainer.d.ts +1 -0
- package/dist/components/StreamingMessageContainer.d.ts.map +1 -1
- package/dist/components/StreamingMessageContainer.js +5 -2
- package/dist/components/StreamingMessageContainer.js.map +1 -1
- package/dist/components/ThinkingBlock.d.ts +11 -0
- package/dist/components/ThinkingBlock.d.ts.map +1 -0
- package/dist/components/ThinkingBlock.js +58 -0
- package/dist/components/ThinkingBlock.js.map +1 -0
- package/dist/dialogs/ApiKeyPromptDialog.d.ts +1 -1
- package/dist/dialogs/ApiKeyPromptDialog.d.ts.map +1 -1
- package/dist/dialogs/ApiKeyPromptDialog.js +3 -1
- package/dist/dialogs/ApiKeyPromptDialog.js.map +1 -1
- package/dist/dialogs/AttachmentOverlay.d.ts.map +1 -1
- package/dist/dialogs/AttachmentOverlay.js +3 -2
- package/dist/dialogs/AttachmentOverlay.js.map +1 -1
- package/dist/dialogs/CustomProviderDialog.d.ts +25 -0
- package/dist/dialogs/CustomProviderDialog.d.ts.map +1 -0
- package/dist/dialogs/CustomProviderDialog.js +270 -0
- package/dist/dialogs/CustomProviderDialog.js.map +1 -0
- package/dist/dialogs/ModelSelector.d.ts +6 -6
- package/dist/dialogs/ModelSelector.d.ts.map +1 -1
- package/dist/dialogs/ModelSelector.js +60 -74
- package/dist/dialogs/ModelSelector.js.map +1 -1
- package/dist/dialogs/PersistentStorageDialog.d.ts +1 -1
- package/dist/dialogs/PersistentStorageDialog.d.ts.map +1 -1
- package/dist/dialogs/PersistentStorageDialog.js +4 -1
- package/dist/dialogs/PersistentStorageDialog.js.map +1 -1
- package/dist/dialogs/ProvidersModelsTab.d.ts +20 -0
- package/dist/dialogs/ProvidersModelsTab.d.ts.map +1 -0
- package/dist/dialogs/ProvidersModelsTab.js +191 -0
- package/dist/dialogs/ProvidersModelsTab.js.map +1 -0
- package/dist/dialogs/SessionListDialog.d.ts +1 -1
- package/dist/dialogs/SessionListDialog.d.ts.map +1 -1
- package/dist/dialogs/SessionListDialog.js +3 -1
- package/dist/dialogs/SessionListDialog.js.map +1 -1
- package/dist/dialogs/SettingsDialog.d.ts +1 -2
- package/dist/dialogs/SettingsDialog.d.ts.map +1 -1
- package/dist/dialogs/SettingsDialog.js +10 -3
- package/dist/dialogs/SettingsDialog.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/storage/app-storage.d.ts +3 -1
- package/dist/storage/app-storage.d.ts.map +1 -1
- package/dist/storage/app-storage.js +2 -1
- package/dist/storage/app-storage.js.map +1 -1
- package/dist/storage/stores/custom-providers-store.d.ts +25 -0
- package/dist/storage/stores/custom-providers-store.d.ts.map +1 -0
- package/dist/storage/stores/custom-providers-store.js +35 -0
- package/dist/storage/stores/custom-providers-store.js.map +1 -0
- package/dist/storage/stores/sessions-store.d.ts.map +1 -1
- package/dist/storage/stores/sessions-store.js +0 -1
- package/dist/storage/stores/sessions-store.js.map +1 -1
- package/dist/storage/types.d.ts +0 -2
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/tools/artifacts/ArtifactPill.d.ts +1 -1
- package/dist/tools/artifacts/ArtifactPill.d.ts.map +1 -1
- package/dist/tools/artifacts/ArtifactPill.js +2 -1
- package/dist/tools/artifacts/ArtifactPill.js.map +1 -1
- package/dist/tools/artifacts/DocxArtifact.js +1 -1
- package/dist/tools/artifacts/DocxArtifact.js.map +1 -1
- package/dist/tools/artifacts/ExcelArtifact.js +1 -1
- package/dist/tools/artifacts/ExcelArtifact.js.map +1 -1
- package/dist/tools/artifacts/GenericArtifact.js +1 -1
- package/dist/tools/artifacts/GenericArtifact.js.map +1 -1
- package/dist/tools/artifacts/HtmlArtifact.d.ts.map +1 -1
- package/dist/tools/artifacts/HtmlArtifact.js +5 -1
- package/dist/tools/artifacts/HtmlArtifact.js.map +1 -1
- package/dist/tools/artifacts/ImageArtifact.js +1 -1
- package/dist/tools/artifacts/ImageArtifact.js.map +1 -1
- package/dist/tools/artifacts/MarkdownArtifact.d.ts.map +1 -1
- package/dist/tools/artifacts/MarkdownArtifact.js +3 -1
- package/dist/tools/artifacts/MarkdownArtifact.js.map +1 -1
- package/dist/tools/artifacts/PdfArtifact.js +1 -1
- package/dist/tools/artifacts/PdfArtifact.js.map +1 -1
- package/dist/tools/artifacts/SvgArtifact.d.ts.map +1 -1
- package/dist/tools/artifacts/SvgArtifact.js +3 -1
- package/dist/tools/artifacts/SvgArtifact.js.map +1 -1
- package/dist/tools/artifacts/TextArtifact.d.ts.map +1 -1
- package/dist/tools/artifacts/TextArtifact.js +2 -1
- package/dist/tools/artifacts/TextArtifact.js.map +1 -1
- package/dist/tools/artifacts/artifacts-tool-renderer.d.ts.map +1 -1
- package/dist/tools/artifacts/artifacts-tool-renderer.js +18 -8
- package/dist/tools/artifacts/artifacts-tool-renderer.js.map +1 -1
- package/dist/tools/artifacts/artifacts.d.ts.map +1 -1
- package/dist/tools/artifacts/artifacts.js +3 -2
- package/dist/tools/artifacts/artifacts.js.map +1 -1
- package/dist/tools/extract-document.d.ts.map +1 -1
- package/dist/tools/extract-document.js +78 -58
- package/dist/tools/extract-document.js.map +1 -1
- package/dist/tools/javascript-repl.d.ts.map +1 -1
- package/dist/tools/javascript-repl.js +7 -3
- package/dist/tools/javascript-repl.js.map +1 -1
- package/dist/tools/renderer-registry.d.ts +1 -1
- package/dist/tools/renderer-registry.d.ts.map +1 -1
- package/dist/tools/renderer-registry.js +20 -6
- package/dist/tools/renderer-registry.js.map +1 -1
- package/dist/tools/renderers/BashRenderer.d.ts.map +1 -1
- package/dist/tools/renderers/BashRenderer.js +5 -2
- package/dist/tools/renderers/BashRenderer.js.map +1 -1
- package/dist/tools/renderers/CalculateRenderer.d.ts.map +1 -1
- package/dist/tools/renderers/CalculateRenderer.js +5 -2
- package/dist/tools/renderers/CalculateRenderer.js.map +1 -1
- package/dist/tools/renderers/DefaultRenderer.d.ts.map +1 -1
- package/dist/tools/renderers/DefaultRenderer.js +5 -2
- package/dist/tools/renderers/DefaultRenderer.js.map +1 -1
- package/dist/tools/renderers/GetCurrentTimeRenderer.d.ts.map +1 -1
- package/dist/tools/renderers/GetCurrentTimeRenderer.js +9 -3
- package/dist/tools/renderers/GetCurrentTimeRenderer.js.map +1 -1
- package/dist/utils/auth-token.js +1 -1
- package/dist/utils/auth-token.js.map +1 -1
- package/dist/utils/i18n.d.ts +105 -3
- package/dist/utils/i18n.d.ts.map +1 -1
- package/dist/utils/i18n.js +72 -2
- package/dist/utils/i18n.js.map +1 -1
- package/dist/utils/model-discovery.d.ts +38 -0
- package/dist/utils/model-discovery.d.ts.map +1 -0
- package/dist/utils/model-discovery.js +243 -0
- package/dist/utils/model-discovery.js.map +1 -0
- package/dist/utils/proxy-utils.d.ts +37 -0
- package/dist/utils/proxy-utils.d.ts.map +1 -0
- package/dist/utils/proxy-utils.js +97 -0
- package/dist/utils/proxy-utils.js.map +1 -0
- package/example/package.json +2 -2
- package/example/src/custom-messages.ts +1 -1
- package/example/src/main.ts +17 -6
- package/package.json +9 -8
- package/src/ChatPanel.ts +4 -2
- package/src/agent/transports/ProviderTransport.ts +5 -10
- package/src/app.css +24 -0
- package/src/components/AgentInterface.ts +14 -3
- package/src/components/AttachmentTile.ts +2 -1
- package/src/components/ConsoleBlock.ts +2 -1
- package/src/components/CustomProviderCard.ts +100 -0
- package/src/components/Input.ts +2 -1
- package/src/components/MessageEditor.ts +6 -33
- package/src/components/MessageList.ts +4 -3
- package/src/components/Messages.ts +32 -20
- package/src/components/ProviderKeyInput.ts +19 -38
- package/src/components/StreamingMessageContainer.ts +3 -2
- package/src/components/ThinkingBlock.ts +43 -0
- package/src/dialogs/ApiKeyPromptDialog.ts +3 -1
- package/src/dialogs/AttachmentOverlay.ts +3 -2
- package/src/dialogs/CustomProviderDialog.ts +274 -0
- package/src/dialogs/ModelSelector.ts +61 -75
- package/src/dialogs/PersistentStorageDialog.ts +4 -1
- package/src/dialogs/ProvidersModelsTab.ts +212 -0
- package/src/dialogs/SessionListDialog.ts +3 -1
- package/src/dialogs/SettingsDialog.ts +10 -13
- package/src/index.ts +8 -0
- package/src/storage/app-storage.ts +4 -0
- package/src/storage/stores/custom-providers-store.ts +62 -0
- package/src/storage/stores/sessions-store.ts +0 -1
- package/src/storage/types.ts +0 -3
- package/src/tools/artifacts/ArtifactPill.ts +2 -1
- package/src/tools/artifacts/DocxArtifact.ts +1 -1
- package/src/tools/artifacts/ExcelArtifact.ts +1 -1
- package/src/tools/artifacts/GenericArtifact.ts +1 -1
- package/src/tools/artifacts/HtmlArtifact.ts +5 -1
- package/src/tools/artifacts/ImageArtifact.ts +1 -1
- package/src/tools/artifacts/MarkdownArtifact.ts +3 -1
- package/src/tools/artifacts/PdfArtifact.ts +1 -1
- package/src/tools/artifacts/SvgArtifact.ts +3 -1
- package/src/tools/artifacts/TextArtifact.ts +2 -1
- package/src/tools/artifacts/artifacts-tool-renderer.ts +20 -8
- package/src/tools/artifacts/artifacts.ts +3 -2
- package/src/tools/extract-document.ts +82 -61
- package/src/tools/javascript-repl.ts +8 -3
- package/src/tools/renderer-registry.ts +20 -6
- package/src/tools/renderers/BashRenderer.ts +6 -2
- package/src/tools/renderers/CalculateRenderer.ts +6 -2
- package/src/tools/renderers/DefaultRenderer.ts +6 -2
- package/src/tools/renderers/GetCurrentTimeRenderer.ts +11 -3
- package/src/utils/auth-token.ts +1 -1
- package/src/utils/i18n.ts +120 -5
- package/src/utils/model-discovery.ts +277 -0
- package/src/utils/proxy-utils.ts +112 -0
- package/example/package-lock.json +0 -1965
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { icon } from "@mariozechner/mini-lit";
|
|
2
|
+
import { html, type TemplateResult } from "lit";
|
|
2
3
|
import type { Ref } from "lit/directives/ref.js";
|
|
3
4
|
import { ref } from "lit/directives/ref.js";
|
|
4
|
-
import {
|
|
5
|
+
import { ChevronsUpDown, ChevronUp, Loader } from "lucide";
|
|
5
6
|
import type { ToolRenderer } from "./types.js";
|
|
6
7
|
|
|
7
8
|
// Registry of tool renderers
|
|
@@ -85,11 +86,23 @@ export function renderCollapsibleHeader(
|
|
|
85
86
|
if (isCollapsed) {
|
|
86
87
|
content.classList.remove("max-h-0");
|
|
87
88
|
content.classList.add("max-h-[2000px]", "mt-3");
|
|
88
|
-
|
|
89
|
+
// Show ChevronUp, hide ChevronsUpDown
|
|
90
|
+
const upIcon = chevron.querySelector(".chevron-up");
|
|
91
|
+
const downIcon = chevron.querySelector(".chevrons-up-down");
|
|
92
|
+
if (upIcon && downIcon) {
|
|
93
|
+
upIcon.classList.remove("hidden");
|
|
94
|
+
downIcon.classList.add("hidden");
|
|
95
|
+
}
|
|
89
96
|
} else {
|
|
90
97
|
content.classList.remove("max-h-[2000px]", "mt-3");
|
|
91
98
|
content.classList.add("max-h-0");
|
|
92
|
-
|
|
99
|
+
// Show ChevronsUpDown, hide ChevronUp
|
|
100
|
+
const upIcon = chevron.querySelector(".chevron-up");
|
|
101
|
+
const downIcon = chevron.querySelector(".chevrons-up-down");
|
|
102
|
+
if (upIcon && downIcon) {
|
|
103
|
+
upIcon.classList.add("hidden");
|
|
104
|
+
downIcon.classList.remove("hidden");
|
|
105
|
+
}
|
|
93
106
|
}
|
|
94
107
|
}
|
|
95
108
|
};
|
|
@@ -108,8 +121,9 @@ export function renderCollapsibleHeader(
|
|
|
108
121
|
${statusIcon(toolIcon, toolIconColor)}
|
|
109
122
|
${text}
|
|
110
123
|
</div>
|
|
111
|
-
<span class="inline-block text-muted-foreground
|
|
112
|
-
${icon(
|
|
124
|
+
<span class="inline-block text-muted-foreground" ${ref(chevronRef)}>
|
|
125
|
+
<span class="chevron-up ${defaultExpanded ? "" : "hidden"}">${icon(ChevronUp, "sm")}</span>
|
|
126
|
+
<span class="chevrons-up-down ${defaultExpanded ? "hidden" : ""}">${icon(ChevronsUpDown, "sm")}</span>
|
|
113
127
|
</span>
|
|
114
128
|
</button>
|
|
115
129
|
`;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { html } from "@mariozechner/mini-lit";
|
|
2
1
|
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
|
2
|
+
import { html } from "lit";
|
|
3
3
|
import { SquareTerminal } from "lucide";
|
|
4
4
|
import { i18n } from "../../utils/i18n.js";
|
|
5
5
|
import { renderHeader } from "../renderer-registry.js";
|
|
@@ -16,7 +16,11 @@ export class BashRenderer implements ToolRenderer<BashParams, undefined> {
|
|
|
16
16
|
|
|
17
17
|
// With result: show command + output
|
|
18
18
|
if (result && params?.command) {
|
|
19
|
-
const output =
|
|
19
|
+
const output =
|
|
20
|
+
result.content
|
|
21
|
+
?.filter((c) => c.type === "text")
|
|
22
|
+
.map((c: any) => c.text)
|
|
23
|
+
.join("\n") || "";
|
|
20
24
|
const combined = output ? `> ${params.command}\n\n${output}` : `> ${params.command}`;
|
|
21
25
|
return {
|
|
22
26
|
content: html`
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { html } from "@mariozechner/mini-lit";
|
|
2
1
|
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
|
2
|
+
import { html } from "lit";
|
|
3
3
|
import { Calculator } from "lucide";
|
|
4
4
|
import { i18n } from "../../utils/i18n.js";
|
|
5
5
|
import { renderHeader } from "../renderer-registry.js";
|
|
@@ -16,7 +16,11 @@ export class CalculateRenderer implements ToolRenderer<CalculateParams, undefine
|
|
|
16
16
|
|
|
17
17
|
// Full params + full result
|
|
18
18
|
if (result && params?.expression) {
|
|
19
|
-
const output =
|
|
19
|
+
const output =
|
|
20
|
+
result.content
|
|
21
|
+
?.filter((c) => c.type === "text")
|
|
22
|
+
.map((c: any) => c.text)
|
|
23
|
+
.join("\n") || "";
|
|
20
24
|
|
|
21
25
|
// Error: show expression in header, error below
|
|
22
26
|
if (result.isError) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { html } from "@mariozechner/mini-lit";
|
|
2
1
|
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
|
2
|
+
import { html } from "lit";
|
|
3
3
|
import { Code } from "lucide";
|
|
4
4
|
import { i18n } from "../../utils/i18n.js";
|
|
5
5
|
import { renderHeader } from "../renderer-registry.js";
|
|
@@ -25,7 +25,11 @@ export class DefaultRenderer implements ToolRenderer {
|
|
|
25
25
|
|
|
26
26
|
// With result: show header + params + result
|
|
27
27
|
if (result) {
|
|
28
|
-
let outputJson =
|
|
28
|
+
let outputJson =
|
|
29
|
+
result.content
|
|
30
|
+
?.filter((c) => c.type === "text")
|
|
31
|
+
.map((c: any) => c.text)
|
|
32
|
+
.join("\n") || i18n("(no output)");
|
|
29
33
|
let outputLanguage = "text";
|
|
30
34
|
|
|
31
35
|
// Try to parse and pretty-print if it's valid JSON
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { html } from "@mariozechner/mini-lit";
|
|
2
1
|
import type { ToolResultMessage } from "@mariozechner/pi-ai";
|
|
2
|
+
import { html } from "lit";
|
|
3
3
|
import { Clock } from "lucide";
|
|
4
4
|
import { i18n } from "../../utils/i18n.js";
|
|
5
5
|
import { renderHeader } from "../renderer-registry.js";
|
|
@@ -19,7 +19,11 @@ export class GetCurrentTimeRenderer implements ToolRenderer<GetCurrentTimeParams
|
|
|
19
19
|
|
|
20
20
|
// Full params + full result
|
|
21
21
|
if (result && params) {
|
|
22
|
-
const output =
|
|
22
|
+
const output =
|
|
23
|
+
result.content
|
|
24
|
+
?.filter((c) => c.type === "text")
|
|
25
|
+
.map((c: any) => c.text)
|
|
26
|
+
.join("\n") || "";
|
|
23
27
|
const headerText = params.timezone
|
|
24
28
|
? `${i18n("Getting current time in")} ${params.timezone}`
|
|
25
29
|
: i18n("Getting current date and time");
|
|
@@ -43,7 +47,11 @@ export class GetCurrentTimeRenderer implements ToolRenderer<GetCurrentTimeParams
|
|
|
43
47
|
|
|
44
48
|
// Full result, no params
|
|
45
49
|
if (result) {
|
|
46
|
-
const output =
|
|
50
|
+
const output =
|
|
51
|
+
result.content
|
|
52
|
+
?.filter((c) => c.type === "text")
|
|
53
|
+
.map((c: any) => c.text)
|
|
54
|
+
.join("\n") || "";
|
|
47
55
|
|
|
48
56
|
// Error: show header, error below
|
|
49
57
|
if (result.isError) {
|
package/src/utils/auth-token.ts
CHANGED
package/src/utils/i18n.ts
CHANGED
|
@@ -137,11 +137,12 @@ declare module "@mariozechner/mini-lit" {
|
|
|
137
137
|
Proxy: string;
|
|
138
138
|
"Use CORS Proxy": string;
|
|
139
139
|
"Proxy URL": string;
|
|
140
|
+
"Format: The proxy must accept requests as <proxy-url>/?url=<target-url>": string;
|
|
140
141
|
"Settings are stored locally in your browser": string;
|
|
141
142
|
Clear: string;
|
|
142
143
|
"API Key Required": string;
|
|
143
144
|
"Enter your API key for {provider}": string;
|
|
144
|
-
"
|
|
145
|
+
"Allows browser-based apps to bypass CORS restrictions when calling LLM providers. Required for Z-AI and Anthropic with OAuth token.": string;
|
|
145
146
|
Off: string;
|
|
146
147
|
Minimal: string;
|
|
147
148
|
Low: string;
|
|
@@ -169,6 +170,40 @@ declare module "@mariozechner/mini-lit" {
|
|
|
169
170
|
messages: string;
|
|
170
171
|
tokens: string;
|
|
171
172
|
"Drop files here": string;
|
|
173
|
+
// Providers & Models
|
|
174
|
+
"Providers & Models": string;
|
|
175
|
+
"Cloud Providers": string;
|
|
176
|
+
"Cloud LLM providers with predefined models. API keys are stored locally in your browser.": string;
|
|
177
|
+
"Custom Providers": string;
|
|
178
|
+
"User-configured servers with auto-discovered or manually defined models.": string;
|
|
179
|
+
"Add Provider": string;
|
|
180
|
+
"No custom providers configured. Click 'Add Provider' to get started.": string;
|
|
181
|
+
Models: string;
|
|
182
|
+
"auto-discovered": string;
|
|
183
|
+
Refresh: string;
|
|
184
|
+
Edit: string;
|
|
185
|
+
"Are you sure you want to delete this provider?": string;
|
|
186
|
+
"Edit Provider": string;
|
|
187
|
+
"Provider Name": string;
|
|
188
|
+
"e.g., My Ollama Server": string;
|
|
189
|
+
"Provider Type": string;
|
|
190
|
+
"Base URL": string;
|
|
191
|
+
"e.g., http://localhost:11434": string;
|
|
192
|
+
"API Key (Optional)": string;
|
|
193
|
+
"Leave empty if not required": string;
|
|
194
|
+
"Test Connection": string;
|
|
195
|
+
Discovered: string;
|
|
196
|
+
models: string;
|
|
197
|
+
and: string;
|
|
198
|
+
more: string;
|
|
199
|
+
"For manual provider types, add models after saving the provider.": string;
|
|
200
|
+
"Please fill in all required fields": string;
|
|
201
|
+
"Failed to save provider": string;
|
|
202
|
+
"OpenAI Completions Compatible": string;
|
|
203
|
+
"OpenAI Responses Compatible": string;
|
|
204
|
+
"Anthropic Messages Compatible": string;
|
|
205
|
+
"Checking...": string;
|
|
206
|
+
Disconnected: string;
|
|
172
207
|
}
|
|
173
208
|
}
|
|
174
209
|
|
|
@@ -312,12 +347,14 @@ export const translations = {
|
|
|
312
347
|
Proxy: "Proxy",
|
|
313
348
|
"Use CORS Proxy": "Use CORS Proxy",
|
|
314
349
|
"Proxy URL": "Proxy URL",
|
|
350
|
+
"Format: The proxy must accept requests as <proxy-url>/?url=<target-url>":
|
|
351
|
+
"Format: The proxy must accept requests as <proxy-url>/?url=<target-url>",
|
|
315
352
|
"Settings are stored locally in your browser": "Settings are stored locally in your browser",
|
|
316
353
|
Clear: "Clear",
|
|
317
354
|
"API Key Required": "API Key Required",
|
|
318
355
|
"Enter your API key for {provider}": "Enter your API key for {provider}",
|
|
319
|
-
"
|
|
320
|
-
"
|
|
356
|
+
"Allows browser-based apps to bypass CORS restrictions when calling LLM providers. Required for Z-AI and Anthropic with OAuth token.":
|
|
357
|
+
"Allows browser-based apps to bypass CORS restrictions when calling LLM providers. Required for Z-AI and Anthropic with OAuth token.",
|
|
321
358
|
Off: "Off",
|
|
322
359
|
Minimal: "Minimal",
|
|
323
360
|
Low: "Low",
|
|
@@ -351,6 +388,44 @@ export const translations = {
|
|
|
351
388
|
Delete: "Delete",
|
|
352
389
|
"Drop files here": "Drop files here",
|
|
353
390
|
"Command failed:": "Command failed:",
|
|
391
|
+
// Providers & Models
|
|
392
|
+
"Providers & Models": "Providers & Models",
|
|
393
|
+
"Cloud Providers": "Cloud Providers",
|
|
394
|
+
"Cloud LLM providers with predefined models. API keys are stored locally in your browser.":
|
|
395
|
+
"Cloud LLM providers with predefined models. API keys are stored locally in your browser.",
|
|
396
|
+
"Custom Providers": "Custom Providers",
|
|
397
|
+
"User-configured servers with auto-discovered or manually defined models.":
|
|
398
|
+
"User-configured servers with auto-discovered or manually defined models.",
|
|
399
|
+
"Add Provider": "Add Provider",
|
|
400
|
+
"No custom providers configured. Click 'Add Provider' to get started.":
|
|
401
|
+
"No custom providers configured. Click 'Add Provider' to get started.",
|
|
402
|
+
"auto-discovered": "auto-discovered",
|
|
403
|
+
Refresh: "Refresh",
|
|
404
|
+
Edit: "Edit",
|
|
405
|
+
"Are you sure you want to delete this provider?": "Are you sure you want to delete this provider?",
|
|
406
|
+
"Edit Provider": "Edit Provider",
|
|
407
|
+
"Provider Name": "Provider Name",
|
|
408
|
+
"e.g., My Ollama Server": "e.g., My Ollama Server",
|
|
409
|
+
"Provider Type": "Provider Type",
|
|
410
|
+
"Base URL": "Base URL",
|
|
411
|
+
"e.g., http://localhost:11434": "e.g., http://localhost:11434",
|
|
412
|
+
"API Key (Optional)": "API Key (Optional)",
|
|
413
|
+
"Leave empty if not required": "Leave empty if not required",
|
|
414
|
+
"Test Connection": "Test Connection",
|
|
415
|
+
Discovered: "Discovered",
|
|
416
|
+
Models: "Models",
|
|
417
|
+
models: "models",
|
|
418
|
+
and: "and",
|
|
419
|
+
more: "more",
|
|
420
|
+
"For manual provider types, add models after saving the provider.":
|
|
421
|
+
"For manual provider types, add models after saving the provider.",
|
|
422
|
+
"Please fill in all required fields": "Please fill in all required fields",
|
|
423
|
+
"Failed to save provider": "Failed to save provider",
|
|
424
|
+
"OpenAI Completions Compatible": "OpenAI Completions Compatible",
|
|
425
|
+
"OpenAI Responses Compatible": "OpenAI Responses Compatible",
|
|
426
|
+
"Anthropic Messages Compatible": "Anthropic Messages Compatible",
|
|
427
|
+
"Checking...": "Checking...",
|
|
428
|
+
Disconnected: "Disconnected",
|
|
354
429
|
},
|
|
355
430
|
de: {
|
|
356
431
|
...defaultGerman,
|
|
@@ -491,12 +566,14 @@ export const translations = {
|
|
|
491
566
|
Proxy: "Proxy",
|
|
492
567
|
"Use CORS Proxy": "CORS-Proxy verwenden",
|
|
493
568
|
"Proxy URL": "Proxy-URL",
|
|
569
|
+
"Format: The proxy must accept requests as <proxy-url>/?url=<target-url>":
|
|
570
|
+
"Format: Der Proxy muss Anfragen als <proxy-url>/?url=<ziel-url> akzeptieren",
|
|
494
571
|
"Settings are stored locally in your browser": "Einstellungen werden lokal in Ihrem Browser gespeichert",
|
|
495
572
|
Clear: "Löschen",
|
|
496
573
|
"API Key Required": "API-Schlüssel erforderlich",
|
|
497
574
|
"Enter your API key for {provider}": "Geben Sie Ihren API-Schlüssel für {provider} ein",
|
|
498
|
-
"
|
|
499
|
-
"
|
|
575
|
+
"Allows browser-based apps to bypass CORS restrictions when calling LLM providers. Required for Z-AI and Anthropic with OAuth token.":
|
|
576
|
+
"Ermöglicht browserbasierten Anwendungen, CORS-Einschränkungen beim Aufruf von LLM-Anbietern zu umgehen. Erforderlich für Z-AI und Anthropic mit OAuth-Token.",
|
|
500
577
|
Off: "Aus",
|
|
501
578
|
Minimal: "Minimal",
|
|
502
579
|
Low: "Niedrig",
|
|
@@ -530,6 +607,44 @@ export const translations = {
|
|
|
530
607
|
Delete: "Löschen",
|
|
531
608
|
"Drop files here": "Dateien hier ablegen",
|
|
532
609
|
"Command failed:": "Befehl fehlgeschlagen:",
|
|
610
|
+
// Providers & Models
|
|
611
|
+
"Providers & Models": "Anbieter & Modelle",
|
|
612
|
+
"Cloud Providers": "Cloud-Anbieter",
|
|
613
|
+
"Cloud LLM providers with predefined models. API keys are stored locally in your browser.":
|
|
614
|
+
"Cloud-LLM-Anbieter mit vordefinierten Modellen. API-Schlüssel werden lokal in Ihrem Browser gespeichert.",
|
|
615
|
+
"Custom Providers": "Benutzerdefinierte Anbieter",
|
|
616
|
+
"User-configured servers with auto-discovered or manually defined models.":
|
|
617
|
+
"Benutzerkonfigurierte Server mit automatisch erkannten oder manuell definierten Modellen.",
|
|
618
|
+
"Add Provider": "Anbieter hinzufügen",
|
|
619
|
+
"No custom providers configured. Click 'Add Provider' to get started.":
|
|
620
|
+
"Keine benutzerdefinierten Anbieter konfiguriert. Klicken Sie auf 'Anbieter hinzufügen', um zu beginnen.",
|
|
621
|
+
"auto-discovered": "automatisch erkannt",
|
|
622
|
+
Refresh: "Aktualisieren",
|
|
623
|
+
Edit: "Bearbeiten",
|
|
624
|
+
"Are you sure you want to delete this provider?": "Sind Sie sicher, dass Sie diesen Anbieter löschen möchten?",
|
|
625
|
+
"Edit Provider": "Anbieter bearbeiten",
|
|
626
|
+
"Provider Name": "Anbietername",
|
|
627
|
+
"e.g., My Ollama Server": "z.B. Mein Ollama Server",
|
|
628
|
+
"Provider Type": "Anbietertyp",
|
|
629
|
+
"Base URL": "Basis-URL",
|
|
630
|
+
"e.g., http://localhost:11434": "z.B. http://localhost:11434",
|
|
631
|
+
"API Key (Optional)": "API-Schlüssel (Optional)",
|
|
632
|
+
"Leave empty if not required": "Leer lassen, falls nicht erforderlich",
|
|
633
|
+
"Test Connection": "Verbindung testen",
|
|
634
|
+
Discovered: "Erkannt",
|
|
635
|
+
Models: "Modelle",
|
|
636
|
+
models: "Modelle",
|
|
637
|
+
and: "und",
|
|
638
|
+
more: "mehr",
|
|
639
|
+
"For manual provider types, add models after saving the provider.":
|
|
640
|
+
"Für manuelle Anbietertypen fügen Sie Modelle nach dem Speichern des Anbieters hinzu.",
|
|
641
|
+
"Please fill in all required fields": "Bitte füllen Sie alle erforderlichen Felder aus",
|
|
642
|
+
"Failed to save provider": "Fehler beim Speichern des Anbieters",
|
|
643
|
+
"OpenAI Completions Compatible": "OpenAI Completions Kompatibel",
|
|
644
|
+
"OpenAI Responses Compatible": "OpenAI Responses Kompatibel",
|
|
645
|
+
"Anthropic Messages Compatible": "Anthropic Messages Kompatibel",
|
|
646
|
+
"Checking...": "Überprüfe...",
|
|
647
|
+
Disconnected: "Getrennt",
|
|
533
648
|
},
|
|
534
649
|
};
|
|
535
650
|
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { LMStudioClient } from "@lmstudio/sdk";
|
|
2
|
+
import type { Model } from "@mariozechner/pi-ai";
|
|
3
|
+
import { Ollama } from "ollama/browser";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Discover models from an Ollama server.
|
|
7
|
+
* @param baseUrl - Base URL of the Ollama server (e.g., "http://localhost:11434")
|
|
8
|
+
* @param apiKey - Optional API key (currently unused by Ollama)
|
|
9
|
+
* @returns Array of discovered models
|
|
10
|
+
*/
|
|
11
|
+
export async function discoverOllamaModels(baseUrl: string, _apiKey?: string): Promise<Model<any>[]> {
|
|
12
|
+
try {
|
|
13
|
+
// Create Ollama client
|
|
14
|
+
const ollama = new Ollama({ host: baseUrl });
|
|
15
|
+
|
|
16
|
+
// Get list of available models
|
|
17
|
+
const { models } = await ollama.list();
|
|
18
|
+
|
|
19
|
+
// Fetch details for each model and convert to Model format
|
|
20
|
+
const ollamaModelPromises: Promise<Model<any> | null>[] = models.map(async (model: any) => {
|
|
21
|
+
try {
|
|
22
|
+
// Get model details
|
|
23
|
+
const details = await ollama.show({
|
|
24
|
+
model: model.name,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Check capabilities - filter out models that don't support tools
|
|
28
|
+
const capabilities: string[] = (details as any).capabilities || [];
|
|
29
|
+
if (!capabilities.includes("tools")) {
|
|
30
|
+
console.debug(`Skipping model ${model.name}: does not support tools`);
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Extract model info
|
|
35
|
+
const modelInfo: any = details.model_info || {};
|
|
36
|
+
|
|
37
|
+
// Get context window size - look for architecture-specific keys
|
|
38
|
+
const architecture = modelInfo["general.architecture"] || "";
|
|
39
|
+
const contextKey = `${architecture}.context_length`;
|
|
40
|
+
const contextWindow = parseInt(modelInfo[contextKey] || "8192", 10);
|
|
41
|
+
|
|
42
|
+
// Ollama caps max tokens at 10x context length
|
|
43
|
+
const maxTokens = contextWindow * 10;
|
|
44
|
+
|
|
45
|
+
// Ollama only supports completions API
|
|
46
|
+
const ollamaModel: Model<any> = {
|
|
47
|
+
id: model.name,
|
|
48
|
+
name: model.name,
|
|
49
|
+
api: "openai-completions" as any,
|
|
50
|
+
provider: "", // Will be set by caller
|
|
51
|
+
baseUrl: `${baseUrl}/v1`,
|
|
52
|
+
reasoning: capabilities.includes("thinking"),
|
|
53
|
+
input: ["text"],
|
|
54
|
+
cost: {
|
|
55
|
+
input: 0,
|
|
56
|
+
output: 0,
|
|
57
|
+
cacheRead: 0,
|
|
58
|
+
cacheWrite: 0,
|
|
59
|
+
},
|
|
60
|
+
contextWindow: contextWindow,
|
|
61
|
+
maxTokens: maxTokens,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
return ollamaModel;
|
|
65
|
+
} catch (err) {
|
|
66
|
+
console.error(`Failed to fetch details for model ${model.name}:`, err);
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const results = await Promise.all(ollamaModelPromises);
|
|
72
|
+
return results.filter((m): m is Model<any> => m !== null);
|
|
73
|
+
} catch (err) {
|
|
74
|
+
console.error("Failed to discover Ollama models:", err);
|
|
75
|
+
throw new Error(`Ollama discovery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Discover models from a llama.cpp server via OpenAI-compatible /v1/models endpoint.
|
|
81
|
+
* @param baseUrl - Base URL of the llama.cpp server (e.g., "http://localhost:8080")
|
|
82
|
+
* @param apiKey - Optional API key
|
|
83
|
+
* @returns Array of discovered models
|
|
84
|
+
*/
|
|
85
|
+
export async function discoverLlamaCppModels(baseUrl: string, apiKey?: string): Promise<Model<any>[]> {
|
|
86
|
+
try {
|
|
87
|
+
const headers: HeadersInit = {
|
|
88
|
+
"Content-Type": "application/json",
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
if (apiKey) {
|
|
92
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const response = await fetch(`${baseUrl}/v1/models`, {
|
|
96
|
+
method: "GET",
|
|
97
|
+
headers,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const data = await response.json();
|
|
105
|
+
|
|
106
|
+
if (!data.data || !Array.isArray(data.data)) {
|
|
107
|
+
throw new Error("Invalid response format from llama.cpp server");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return data.data.map((model: any) => {
|
|
111
|
+
// llama.cpp doesn't always provide context window info
|
|
112
|
+
const contextWindow = model.context_length || 8192;
|
|
113
|
+
const maxTokens = model.max_tokens || 4096;
|
|
114
|
+
|
|
115
|
+
const llamaModel: Model<any> = {
|
|
116
|
+
id: model.id,
|
|
117
|
+
name: model.id,
|
|
118
|
+
api: "openai-completions" as any,
|
|
119
|
+
provider: "", // Will be set by caller
|
|
120
|
+
baseUrl: `${baseUrl}/v1`,
|
|
121
|
+
reasoning: false,
|
|
122
|
+
input: ["text"],
|
|
123
|
+
cost: {
|
|
124
|
+
input: 0,
|
|
125
|
+
output: 0,
|
|
126
|
+
cacheRead: 0,
|
|
127
|
+
cacheWrite: 0,
|
|
128
|
+
},
|
|
129
|
+
contextWindow: contextWindow,
|
|
130
|
+
maxTokens: maxTokens,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
return llamaModel;
|
|
134
|
+
});
|
|
135
|
+
} catch (err) {
|
|
136
|
+
console.error("Failed to discover llama.cpp models:", err);
|
|
137
|
+
throw new Error(`llama.cpp discovery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Discover models from a vLLM server via OpenAI-compatible /v1/models endpoint.
|
|
143
|
+
* @param baseUrl - Base URL of the vLLM server (e.g., "http://localhost:8000")
|
|
144
|
+
* @param apiKey - Optional API key
|
|
145
|
+
* @returns Array of discovered models
|
|
146
|
+
*/
|
|
147
|
+
export async function discoverVLLMModels(baseUrl: string, apiKey?: string): Promise<Model<any>[]> {
|
|
148
|
+
try {
|
|
149
|
+
const headers: HeadersInit = {
|
|
150
|
+
"Content-Type": "application/json",
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
if (apiKey) {
|
|
154
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const response = await fetch(`${baseUrl}/v1/models`, {
|
|
158
|
+
method: "GET",
|
|
159
|
+
headers,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
if (!response.ok) {
|
|
163
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const data = await response.json();
|
|
167
|
+
|
|
168
|
+
if (!data.data || !Array.isArray(data.data)) {
|
|
169
|
+
throw new Error("Invalid response format from vLLM server");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return data.data.map((model: any) => {
|
|
173
|
+
// vLLM provides max_model_len which is the context window
|
|
174
|
+
const contextWindow = model.max_model_len || 8192;
|
|
175
|
+
const maxTokens = Math.min(contextWindow, 4096); // Cap max tokens
|
|
176
|
+
|
|
177
|
+
const vllmModel: Model<any> = {
|
|
178
|
+
id: model.id,
|
|
179
|
+
name: model.id,
|
|
180
|
+
api: "openai-completions" as any,
|
|
181
|
+
provider: "", // Will be set by caller
|
|
182
|
+
baseUrl: `${baseUrl}/v1`,
|
|
183
|
+
reasoning: false,
|
|
184
|
+
input: ["text"],
|
|
185
|
+
cost: {
|
|
186
|
+
input: 0,
|
|
187
|
+
output: 0,
|
|
188
|
+
cacheRead: 0,
|
|
189
|
+
cacheWrite: 0,
|
|
190
|
+
},
|
|
191
|
+
contextWindow: contextWindow,
|
|
192
|
+
maxTokens: maxTokens,
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
return vllmModel;
|
|
196
|
+
});
|
|
197
|
+
} catch (err) {
|
|
198
|
+
console.error("Failed to discover vLLM models:", err);
|
|
199
|
+
throw new Error(`vLLM discovery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Discover models from an LM Studio server using the LM Studio SDK.
|
|
205
|
+
* @param baseUrl - Base URL of the LM Studio server (e.g., "http://localhost:1234")
|
|
206
|
+
* @param apiKey - Optional API key (unused for LM Studio SDK)
|
|
207
|
+
* @returns Array of discovered models
|
|
208
|
+
*/
|
|
209
|
+
export async function discoverLMStudioModels(baseUrl: string, _apiKey?: string): Promise<Model<any>[]> {
|
|
210
|
+
try {
|
|
211
|
+
// Extract host and port from baseUrl
|
|
212
|
+
const url = new URL(baseUrl);
|
|
213
|
+
const port = url.port ? parseInt(url.port, 10) : 1234;
|
|
214
|
+
|
|
215
|
+
// Create LM Studio client
|
|
216
|
+
const client = new LMStudioClient({ baseUrl: `ws://${url.hostname}:${port}` });
|
|
217
|
+
|
|
218
|
+
// List all downloaded models
|
|
219
|
+
const models = await client.system.listDownloadedModels();
|
|
220
|
+
|
|
221
|
+
// Filter to only LLM models and map to our Model format
|
|
222
|
+
return models
|
|
223
|
+
.filter((model) => model.type === "llm")
|
|
224
|
+
.map((model) => {
|
|
225
|
+
const contextWindow = model.maxContextLength;
|
|
226
|
+
// Use 10x context length like Ollama does
|
|
227
|
+
const maxTokens = contextWindow;
|
|
228
|
+
|
|
229
|
+
const lmStudioModel: Model<any> = {
|
|
230
|
+
id: model.path,
|
|
231
|
+
name: model.displayName || model.path,
|
|
232
|
+
api: "openai-completions" as any,
|
|
233
|
+
provider: "", // Will be set by caller
|
|
234
|
+
baseUrl: `${baseUrl}/v1`,
|
|
235
|
+
reasoning: model.trainedForToolUse || false,
|
|
236
|
+
input: model.vision ? ["text", "image"] : ["text"],
|
|
237
|
+
cost: {
|
|
238
|
+
input: 0,
|
|
239
|
+
output: 0,
|
|
240
|
+
cacheRead: 0,
|
|
241
|
+
cacheWrite: 0,
|
|
242
|
+
},
|
|
243
|
+
contextWindow: contextWindow,
|
|
244
|
+
maxTokens: maxTokens,
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
return lmStudioModel;
|
|
248
|
+
});
|
|
249
|
+
} catch (err) {
|
|
250
|
+
console.error("Failed to discover LM Studio models:", err);
|
|
251
|
+
throw new Error(`LM Studio discovery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Convenience function to discover models based on provider type.
|
|
257
|
+
* @param type - Provider type
|
|
258
|
+
* @param baseUrl - Base URL of the server
|
|
259
|
+
* @param apiKey - Optional API key
|
|
260
|
+
* @returns Array of discovered models
|
|
261
|
+
*/
|
|
262
|
+
export async function discoverModels(
|
|
263
|
+
type: "ollama" | "llama.cpp" | "vllm" | "lmstudio",
|
|
264
|
+
baseUrl: string,
|
|
265
|
+
apiKey?: string,
|
|
266
|
+
): Promise<Model<any>[]> {
|
|
267
|
+
switch (type) {
|
|
268
|
+
case "ollama":
|
|
269
|
+
return discoverOllamaModels(baseUrl, apiKey);
|
|
270
|
+
case "llama.cpp":
|
|
271
|
+
return discoverLlamaCppModels(baseUrl, apiKey);
|
|
272
|
+
case "vllm":
|
|
273
|
+
return discoverVLLMModels(baseUrl, apiKey);
|
|
274
|
+
case "lmstudio":
|
|
275
|
+
return discoverLMStudioModels(baseUrl, apiKey);
|
|
276
|
+
}
|
|
277
|
+
}
|