@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
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { LMStudioClient } from "@lmstudio/sdk";
|
|
2
|
+
import { Ollama } from "ollama/browser";
|
|
3
|
+
/**
|
|
4
|
+
* Discover models from an Ollama server.
|
|
5
|
+
* @param baseUrl - Base URL of the Ollama server (e.g., "http://localhost:11434")
|
|
6
|
+
* @param apiKey - Optional API key (currently unused by Ollama)
|
|
7
|
+
* @returns Array of discovered models
|
|
8
|
+
*/
|
|
9
|
+
export async function discoverOllamaModels(baseUrl, _apiKey) {
|
|
10
|
+
try {
|
|
11
|
+
// Create Ollama client
|
|
12
|
+
const ollama = new Ollama({ host: baseUrl });
|
|
13
|
+
// Get list of available models
|
|
14
|
+
const { models } = await ollama.list();
|
|
15
|
+
// Fetch details for each model and convert to Model format
|
|
16
|
+
const ollamaModelPromises = models.map(async (model) => {
|
|
17
|
+
try {
|
|
18
|
+
// Get model details
|
|
19
|
+
const details = await ollama.show({
|
|
20
|
+
model: model.name,
|
|
21
|
+
});
|
|
22
|
+
// Check capabilities - filter out models that don't support tools
|
|
23
|
+
const capabilities = details.capabilities || [];
|
|
24
|
+
if (!capabilities.includes("tools")) {
|
|
25
|
+
console.debug(`Skipping model ${model.name}: does not support tools`);
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
// Extract model info
|
|
29
|
+
const modelInfo = details.model_info || {};
|
|
30
|
+
// Get context window size - look for architecture-specific keys
|
|
31
|
+
const architecture = modelInfo["general.architecture"] || "";
|
|
32
|
+
const contextKey = `${architecture}.context_length`;
|
|
33
|
+
const contextWindow = parseInt(modelInfo[contextKey] || "8192", 10);
|
|
34
|
+
// Ollama caps max tokens at 10x context length
|
|
35
|
+
const maxTokens = contextWindow * 10;
|
|
36
|
+
// Ollama only supports completions API
|
|
37
|
+
const ollamaModel = {
|
|
38
|
+
id: model.name,
|
|
39
|
+
name: model.name,
|
|
40
|
+
api: "openai-completions",
|
|
41
|
+
provider: "", // Will be set by caller
|
|
42
|
+
baseUrl: `${baseUrl}/v1`,
|
|
43
|
+
reasoning: capabilities.includes("thinking"),
|
|
44
|
+
input: ["text"],
|
|
45
|
+
cost: {
|
|
46
|
+
input: 0,
|
|
47
|
+
output: 0,
|
|
48
|
+
cacheRead: 0,
|
|
49
|
+
cacheWrite: 0,
|
|
50
|
+
},
|
|
51
|
+
contextWindow: contextWindow,
|
|
52
|
+
maxTokens: maxTokens,
|
|
53
|
+
};
|
|
54
|
+
return ollamaModel;
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
console.error(`Failed to fetch details for model ${model.name}:`, err);
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
const results = await Promise.all(ollamaModelPromises);
|
|
62
|
+
return results.filter((m) => m !== null);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
console.error("Failed to discover Ollama models:", err);
|
|
66
|
+
throw new Error(`Ollama discovery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Discover models from a llama.cpp server via OpenAI-compatible /v1/models endpoint.
|
|
71
|
+
* @param baseUrl - Base URL of the llama.cpp server (e.g., "http://localhost:8080")
|
|
72
|
+
* @param apiKey - Optional API key
|
|
73
|
+
* @returns Array of discovered models
|
|
74
|
+
*/
|
|
75
|
+
export async function discoverLlamaCppModels(baseUrl, apiKey) {
|
|
76
|
+
try {
|
|
77
|
+
const headers = {
|
|
78
|
+
"Content-Type": "application/json",
|
|
79
|
+
};
|
|
80
|
+
if (apiKey) {
|
|
81
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
82
|
+
}
|
|
83
|
+
const response = await fetch(`${baseUrl}/v1/models`, {
|
|
84
|
+
method: "GET",
|
|
85
|
+
headers,
|
|
86
|
+
});
|
|
87
|
+
if (!response.ok) {
|
|
88
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
89
|
+
}
|
|
90
|
+
const data = await response.json();
|
|
91
|
+
if (!data.data || !Array.isArray(data.data)) {
|
|
92
|
+
throw new Error("Invalid response format from llama.cpp server");
|
|
93
|
+
}
|
|
94
|
+
return data.data.map((model) => {
|
|
95
|
+
// llama.cpp doesn't always provide context window info
|
|
96
|
+
const contextWindow = model.context_length || 8192;
|
|
97
|
+
const maxTokens = model.max_tokens || 4096;
|
|
98
|
+
const llamaModel = {
|
|
99
|
+
id: model.id,
|
|
100
|
+
name: model.id,
|
|
101
|
+
api: "openai-completions",
|
|
102
|
+
provider: "", // Will be set by caller
|
|
103
|
+
baseUrl: `${baseUrl}/v1`,
|
|
104
|
+
reasoning: false,
|
|
105
|
+
input: ["text"],
|
|
106
|
+
cost: {
|
|
107
|
+
input: 0,
|
|
108
|
+
output: 0,
|
|
109
|
+
cacheRead: 0,
|
|
110
|
+
cacheWrite: 0,
|
|
111
|
+
},
|
|
112
|
+
contextWindow: contextWindow,
|
|
113
|
+
maxTokens: maxTokens,
|
|
114
|
+
};
|
|
115
|
+
return llamaModel;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
console.error("Failed to discover llama.cpp models:", err);
|
|
120
|
+
throw new Error(`llama.cpp discovery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Discover models from a vLLM server via OpenAI-compatible /v1/models endpoint.
|
|
125
|
+
* @param baseUrl - Base URL of the vLLM server (e.g., "http://localhost:8000")
|
|
126
|
+
* @param apiKey - Optional API key
|
|
127
|
+
* @returns Array of discovered models
|
|
128
|
+
*/
|
|
129
|
+
export async function discoverVLLMModels(baseUrl, apiKey) {
|
|
130
|
+
try {
|
|
131
|
+
const headers = {
|
|
132
|
+
"Content-Type": "application/json",
|
|
133
|
+
};
|
|
134
|
+
if (apiKey) {
|
|
135
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
136
|
+
}
|
|
137
|
+
const response = await fetch(`${baseUrl}/v1/models`, {
|
|
138
|
+
method: "GET",
|
|
139
|
+
headers,
|
|
140
|
+
});
|
|
141
|
+
if (!response.ok) {
|
|
142
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
143
|
+
}
|
|
144
|
+
const data = await response.json();
|
|
145
|
+
if (!data.data || !Array.isArray(data.data)) {
|
|
146
|
+
throw new Error("Invalid response format from vLLM server");
|
|
147
|
+
}
|
|
148
|
+
return data.data.map((model) => {
|
|
149
|
+
// vLLM provides max_model_len which is the context window
|
|
150
|
+
const contextWindow = model.max_model_len || 8192;
|
|
151
|
+
const maxTokens = Math.min(contextWindow, 4096); // Cap max tokens
|
|
152
|
+
const vllmModel = {
|
|
153
|
+
id: model.id,
|
|
154
|
+
name: model.id,
|
|
155
|
+
api: "openai-completions",
|
|
156
|
+
provider: "", // Will be set by caller
|
|
157
|
+
baseUrl: `${baseUrl}/v1`,
|
|
158
|
+
reasoning: false,
|
|
159
|
+
input: ["text"],
|
|
160
|
+
cost: {
|
|
161
|
+
input: 0,
|
|
162
|
+
output: 0,
|
|
163
|
+
cacheRead: 0,
|
|
164
|
+
cacheWrite: 0,
|
|
165
|
+
},
|
|
166
|
+
contextWindow: contextWindow,
|
|
167
|
+
maxTokens: maxTokens,
|
|
168
|
+
};
|
|
169
|
+
return vllmModel;
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
console.error("Failed to discover vLLM models:", err);
|
|
174
|
+
throw new Error(`vLLM discovery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Discover models from an LM Studio server using the LM Studio SDK.
|
|
179
|
+
* @param baseUrl - Base URL of the LM Studio server (e.g., "http://localhost:1234")
|
|
180
|
+
* @param apiKey - Optional API key (unused for LM Studio SDK)
|
|
181
|
+
* @returns Array of discovered models
|
|
182
|
+
*/
|
|
183
|
+
export async function discoverLMStudioModels(baseUrl, _apiKey) {
|
|
184
|
+
try {
|
|
185
|
+
// Extract host and port from baseUrl
|
|
186
|
+
const url = new URL(baseUrl);
|
|
187
|
+
const port = url.port ? parseInt(url.port, 10) : 1234;
|
|
188
|
+
// Create LM Studio client
|
|
189
|
+
const client = new LMStudioClient({ baseUrl: `ws://${url.hostname}:${port}` });
|
|
190
|
+
// List all downloaded models
|
|
191
|
+
const models = await client.system.listDownloadedModels();
|
|
192
|
+
// Filter to only LLM models and map to our Model format
|
|
193
|
+
return models
|
|
194
|
+
.filter((model) => model.type === "llm")
|
|
195
|
+
.map((model) => {
|
|
196
|
+
const contextWindow = model.maxContextLength;
|
|
197
|
+
// Use 10x context length like Ollama does
|
|
198
|
+
const maxTokens = contextWindow;
|
|
199
|
+
const lmStudioModel = {
|
|
200
|
+
id: model.path,
|
|
201
|
+
name: model.displayName || model.path,
|
|
202
|
+
api: "openai-completions",
|
|
203
|
+
provider: "", // Will be set by caller
|
|
204
|
+
baseUrl: `${baseUrl}/v1`,
|
|
205
|
+
reasoning: model.trainedForToolUse || false,
|
|
206
|
+
input: model.vision ? ["text", "image"] : ["text"],
|
|
207
|
+
cost: {
|
|
208
|
+
input: 0,
|
|
209
|
+
output: 0,
|
|
210
|
+
cacheRead: 0,
|
|
211
|
+
cacheWrite: 0,
|
|
212
|
+
},
|
|
213
|
+
contextWindow: contextWindow,
|
|
214
|
+
maxTokens: maxTokens,
|
|
215
|
+
};
|
|
216
|
+
return lmStudioModel;
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
catch (err) {
|
|
220
|
+
console.error("Failed to discover LM Studio models:", err);
|
|
221
|
+
throw new Error(`LM Studio discovery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Convenience function to discover models based on provider type.
|
|
226
|
+
* @param type - Provider type
|
|
227
|
+
* @param baseUrl - Base URL of the server
|
|
228
|
+
* @param apiKey - Optional API key
|
|
229
|
+
* @returns Array of discovered models
|
|
230
|
+
*/
|
|
231
|
+
export async function discoverModels(type, baseUrl, apiKey) {
|
|
232
|
+
switch (type) {
|
|
233
|
+
case "ollama":
|
|
234
|
+
return discoverOllamaModels(baseUrl, apiKey);
|
|
235
|
+
case "llama.cpp":
|
|
236
|
+
return discoverLlamaCppModels(baseUrl, apiKey);
|
|
237
|
+
case "vllm":
|
|
238
|
+
return discoverVLLMModels(baseUrl, apiKey);
|
|
239
|
+
case "lmstudio":
|
|
240
|
+
return discoverLMStudioModels(baseUrl, apiKey);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=model-discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-discovery.js","sourceRoot":"","sources":["../../src/utils/model-discovery.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Api, Model } from "@mariozechner/pi-ai";
|
|
2
|
+
/**
|
|
3
|
+
* Centralized proxy decision logic.
|
|
4
|
+
*
|
|
5
|
+
* Determines whether to use a CORS proxy for LLM API requests based on:
|
|
6
|
+
* - Provider name
|
|
7
|
+
* - API key pattern (for providers where it matters)
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Check if a provider/API key combination requires a CORS proxy.
|
|
11
|
+
*
|
|
12
|
+
* @param provider - Provider name (e.g., "anthropic", "openai", "zai")
|
|
13
|
+
* @param apiKey - API key for the provider
|
|
14
|
+
* @returns true if proxy is required, false otherwise
|
|
15
|
+
*/
|
|
16
|
+
export declare function shouldUseProxyForProvider(provider: string, apiKey: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Apply CORS proxy to a model's baseUrl if needed.
|
|
19
|
+
*
|
|
20
|
+
* @param model - The model to potentially proxy
|
|
21
|
+
* @param apiKey - API key for the provider
|
|
22
|
+
* @param proxyUrl - CORS proxy URL (e.g., "https://proxy.mariozechner.at/proxy")
|
|
23
|
+
* @returns Model with modified baseUrl if proxy is needed, otherwise original model
|
|
24
|
+
*/
|
|
25
|
+
export declare function applyProxyIfNeeded<T extends Api>(model: Model<T>, apiKey: string, proxyUrl?: string): Model<T>;
|
|
26
|
+
/**
|
|
27
|
+
* Check if an error is likely a CORS error.
|
|
28
|
+
*
|
|
29
|
+
* CORS errors in browsers typically manifest as:
|
|
30
|
+
* - TypeError with message "Failed to fetch"
|
|
31
|
+
* - NetworkError
|
|
32
|
+
*
|
|
33
|
+
* @param error - The error to check
|
|
34
|
+
* @returns true if error is likely a CORS error
|
|
35
|
+
*/
|
|
36
|
+
export declare function isCorsError(error: unknown): boolean;
|
|
37
|
+
//# sourceMappingURL=proxy-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-utils.d.ts","sourceRoot":"","sources":["../../src/utils/proxy-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAEtD;;;;;;GAMG;AAEH;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CA2BnF;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAqB9G;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAwBnD"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized proxy decision logic.
|
|
3
|
+
*
|
|
4
|
+
* Determines whether to use a CORS proxy for LLM API requests based on:
|
|
5
|
+
* - Provider name
|
|
6
|
+
* - API key pattern (for providers where it matters)
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Check if a provider/API key combination requires a CORS proxy.
|
|
10
|
+
*
|
|
11
|
+
* @param provider - Provider name (e.g., "anthropic", "openai", "zai")
|
|
12
|
+
* @param apiKey - API key for the provider
|
|
13
|
+
* @returns true if proxy is required, false otherwise
|
|
14
|
+
*/
|
|
15
|
+
export function shouldUseProxyForProvider(provider, apiKey) {
|
|
16
|
+
switch (provider.toLowerCase()) {
|
|
17
|
+
case "zai":
|
|
18
|
+
// Z-AI always requires proxy
|
|
19
|
+
return true;
|
|
20
|
+
case "anthropic":
|
|
21
|
+
// Anthropic OAuth tokens (sk-ant-oat-*) require proxy
|
|
22
|
+
// Regular API keys (sk-ant-api-*) do NOT require proxy
|
|
23
|
+
return apiKey.startsWith("sk-ant-oat");
|
|
24
|
+
// These providers work without proxy
|
|
25
|
+
case "openai":
|
|
26
|
+
case "google":
|
|
27
|
+
case "groq":
|
|
28
|
+
case "openrouter":
|
|
29
|
+
case "cerebras":
|
|
30
|
+
case "xai":
|
|
31
|
+
case "ollama":
|
|
32
|
+
case "lmstudio":
|
|
33
|
+
return false;
|
|
34
|
+
// Unknown providers - assume no proxy needed
|
|
35
|
+
// This allows new providers to work by default
|
|
36
|
+
default:
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Apply CORS proxy to a model's baseUrl if needed.
|
|
42
|
+
*
|
|
43
|
+
* @param model - The model to potentially proxy
|
|
44
|
+
* @param apiKey - API key for the provider
|
|
45
|
+
* @param proxyUrl - CORS proxy URL (e.g., "https://proxy.mariozechner.at/proxy")
|
|
46
|
+
* @returns Model with modified baseUrl if proxy is needed, otherwise original model
|
|
47
|
+
*/
|
|
48
|
+
export function applyProxyIfNeeded(model, apiKey, proxyUrl) {
|
|
49
|
+
// If no proxy URL configured, return original model
|
|
50
|
+
if (!proxyUrl) {
|
|
51
|
+
return model;
|
|
52
|
+
}
|
|
53
|
+
// If model has no baseUrl, can't proxy it
|
|
54
|
+
if (!model.baseUrl) {
|
|
55
|
+
return model;
|
|
56
|
+
}
|
|
57
|
+
// Check if this provider/key needs proxy
|
|
58
|
+
if (!shouldUseProxyForProvider(model.provider, apiKey)) {
|
|
59
|
+
return model;
|
|
60
|
+
}
|
|
61
|
+
// Apply proxy to baseUrl
|
|
62
|
+
return {
|
|
63
|
+
...model,
|
|
64
|
+
baseUrl: `${proxyUrl}/?url=${encodeURIComponent(model.baseUrl)}`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Check if an error is likely a CORS error.
|
|
69
|
+
*
|
|
70
|
+
* CORS errors in browsers typically manifest as:
|
|
71
|
+
* - TypeError with message "Failed to fetch"
|
|
72
|
+
* - NetworkError
|
|
73
|
+
*
|
|
74
|
+
* @param error - The error to check
|
|
75
|
+
* @returns true if error is likely a CORS error
|
|
76
|
+
*/
|
|
77
|
+
export function isCorsError(error) {
|
|
78
|
+
if (!(error instanceof Error)) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
// Check for common CORS error patterns
|
|
82
|
+
const message = error.message.toLowerCase();
|
|
83
|
+
// "Failed to fetch" is the standard CORS error in most browsers
|
|
84
|
+
if (error.name === "TypeError" && message.includes("failed to fetch")) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
// Some browsers report "NetworkError"
|
|
88
|
+
if (error.name === "NetworkError") {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
// CORS-specific messages
|
|
92
|
+
if (message.includes("cors") || message.includes("cross-origin")) {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=proxy-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-utils.js","sourceRoot":"","sources":["../../src/utils/proxy-utils.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,QAAgB,EAAE,MAAc;IACzE,QAAQ,QAAQ,CAAC,WAAW,EAAE,EAAE,CAAC;QAChC,KAAK,KAAK;YACT,6BAA6B;YAC7B,OAAO,IAAI,CAAC;QAEb,KAAK,WAAW;YACf,sDAAsD;YACtD,uDAAuD;YACvD,OAAO,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;QAExC,qCAAqC;QACrC,KAAK,QAAQ,CAAC;QACd,KAAK,QAAQ,CAAC;QACd,KAAK,MAAM,CAAC;QACZ,KAAK,YAAY,CAAC;QAClB,KAAK,UAAU,CAAC;QAChB,KAAK,KAAK,CAAC;QACX,KAAK,QAAQ,CAAC;QACd,KAAK,UAAU;YACd,OAAO,KAAK,CAAC;QAEd,6CAA6C;QAC7C,+CAA+C;QAC/C;YACC,OAAO,KAAK,CAAC;IACf,CAAC;AACF,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAgB,KAAe,EAAE,MAAc,EAAE,QAAiB;IACnG,oDAAoD;IACpD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACf,OAAO,KAAK,CAAC;IACd,CAAC;IAED,0CAA0C;IAC1C,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO,KAAK,CAAC;IACd,CAAC;IAED,yCAAyC;IACzC,IAAI,CAAC,yBAAyB,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC;QACxD,OAAO,KAAK,CAAC;IACd,CAAC;IAED,yBAAyB;IACzB,OAAO;QACN,GAAG,KAAK;QACR,OAAO,EAAE,GAAG,QAAQ,SAAS,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE;KAChE,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CAAC,KAAc;IACzC,IAAI,CAAC,CAAC,KAAK,YAAY,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACd,CAAC;IAED,uCAAuC;IACvC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;IAE5C,gEAAgE;IAChE,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;QACvE,OAAO,IAAI,CAAC;IACb,CAAC;IAED,sCAAsC;IACtC,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC;IACb,CAAC;IAED,yBAAyB;IACzB,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;QAClE,OAAO,IAAI,CAAC;IACb,CAAC;IAED,OAAO,KAAK,CAAC;AACd,CAAC"}
|
package/example/package.json
CHANGED
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
"check": "tsc --noEmit"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@mariozechner/mini-lit": "^0.
|
|
12
|
+
"@mariozechner/mini-lit": "^0.2.0",
|
|
13
13
|
"@mariozechner/pi-ai": "file:../../ai",
|
|
14
14
|
"@mariozechner/pi-web-ui": "file:../",
|
|
15
|
-
"@tailwindcss/vite": "^4.1.
|
|
15
|
+
"@tailwindcss/vite": "^4.1.17",
|
|
16
16
|
"lit": "^3.3.1",
|
|
17
17
|
"lucide": "^0.544.0"
|
|
18
18
|
},
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Alert } from "@mariozechner/mini-lit";
|
|
2
1
|
import type { Message } from "@mariozechner/pi-ai";
|
|
3
2
|
import { html } from "lit";
|
|
4
3
|
import { registerMessageRenderer } from "@mariozechner/pi-web-ui";
|
|
5
4
|
import type { AppMessage, MessageRenderer } from "@mariozechner/pi-web-ui";
|
|
5
|
+
import { Alert } from "@mariozechner/mini-lit/dist/Alert.js";
|
|
6
6
|
|
|
7
7
|
// ============================================================================
|
|
8
8
|
// 1. EXTEND AppMessage TYPE VIA DECLARATION MERGING
|
package/example/src/main.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { Button, icon, Input } from "@mariozechner/mini-lit";
|
|
2
1
|
import "@mariozechner/mini-lit/dist/ThemeToggle.js";
|
|
3
2
|
import { getModel } from "@mariozechner/pi-ai";
|
|
4
3
|
import {
|
|
5
4
|
Agent,
|
|
6
5
|
type AgentState,
|
|
7
6
|
ApiKeyPromptDialog,
|
|
8
|
-
ApiKeysTab,
|
|
9
7
|
type AppMessage,
|
|
10
8
|
AppStorage,
|
|
11
9
|
ChatPanel,
|
|
12
10
|
createJavaScriptReplTool,
|
|
11
|
+
CustomProvidersStore,
|
|
13
12
|
IndexedDBStorageBackend,
|
|
14
13
|
// PersistentStorageDialog, // TODO: Fix - currently broken
|
|
15
14
|
ProviderKeysStore,
|
|
16
15
|
ProviderTransport,
|
|
16
|
+
ProvidersModelsTab,
|
|
17
17
|
ProxyTab,
|
|
18
18
|
SessionListDialog,
|
|
19
19
|
SessionsStore,
|
|
@@ -25,6 +25,9 @@ import { html, render } from "lit";
|
|
|
25
25
|
import { Bell, History, Plus, Settings } from "lucide";
|
|
26
26
|
import "./app.css";
|
|
27
27
|
import { createSystemNotification, customMessageTransformer, registerCustomMessageRenderers } from "./custom-messages.js";
|
|
28
|
+
import { Button } from "@mariozechner/mini-lit/dist/Button.js";
|
|
29
|
+
import { icon } from "@mariozechner/mini-lit";
|
|
30
|
+
import { Input } from "@mariozechner/mini-lit/dist/Input.js";
|
|
28
31
|
|
|
29
32
|
// Register custom message renderers
|
|
30
33
|
registerCustomMessageRenderers();
|
|
@@ -33,24 +36,32 @@ registerCustomMessageRenderers();
|
|
|
33
36
|
const settings = new SettingsStore();
|
|
34
37
|
const providerKeys = new ProviderKeysStore();
|
|
35
38
|
const sessions = new SessionsStore();
|
|
39
|
+
const customProviders = new CustomProvidersStore();
|
|
36
40
|
|
|
37
41
|
// Gather configs
|
|
38
|
-
const configs = [
|
|
42
|
+
const configs = [
|
|
43
|
+
settings.getConfig(),
|
|
44
|
+
SessionsStore.getMetadataConfig(),
|
|
45
|
+
providerKeys.getConfig(),
|
|
46
|
+
customProviders.getConfig(),
|
|
47
|
+
sessions.getConfig(),
|
|
48
|
+
];
|
|
39
49
|
|
|
40
50
|
// Create backend
|
|
41
51
|
const backend = new IndexedDBStorageBackend({
|
|
42
52
|
dbName: "pi-web-ui-example",
|
|
43
|
-
version:
|
|
53
|
+
version: 2, // Incremented for custom-providers store
|
|
44
54
|
stores: configs,
|
|
45
55
|
});
|
|
46
56
|
|
|
47
57
|
// Wire backend to stores
|
|
48
58
|
settings.setBackend(backend);
|
|
49
59
|
providerKeys.setBackend(backend);
|
|
60
|
+
customProviders.setBackend(backend);
|
|
50
61
|
sessions.setBackend(backend);
|
|
51
62
|
|
|
52
63
|
// Create and set app storage
|
|
53
|
-
const storage = new AppStorage(settings, providerKeys, sessions, backend);
|
|
64
|
+
const storage = new AppStorage(settings, providerKeys, sessions, customProviders, backend);
|
|
54
65
|
setAppStorage(storage);
|
|
55
66
|
|
|
56
67
|
let currentSessionId: string | undefined;
|
|
@@ -349,7 +360,7 @@ const renderApp = () => {
|
|
|
349
360
|
variant: "ghost",
|
|
350
361
|
size: "sm",
|
|
351
362
|
children: icon(Settings, "sm"),
|
|
352
|
-
onClick: () => SettingsDialog.open([new
|
|
363
|
+
onClick: () => SettingsDialog.open([new ProvidersModelsTab(), new ProxyTab()]),
|
|
353
364
|
title: "Settings",
|
|
354
365
|
})}
|
|
355
366
|
</div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mariozechner/pi-web-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Reusable web UI components for AI chat interfaces powered by @mariozechner/pi-ai",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -13,25 +13,26 @@
|
|
|
13
13
|
"clean": "rm -rf dist",
|
|
14
14
|
"build": "tsc -p tsconfig.build.json && tailwindcss -i ./src/app.css -o ./dist/app.css --minify",
|
|
15
15
|
"dev": "concurrently --names \"build,example\" --prefix-colors \"cyan,green\" \"tsc -p tsconfig.build.json --watch --preserveWatchOutput\" \"tailwindcss -i ./src/app.css -o ./dist/app.css --watch\" \"npm run dev --prefix example\"",
|
|
16
|
-
"
|
|
17
|
-
"check": "
|
|
16
|
+
"dev:tsc": "concurrently --names \"build\" --prefix-colors \"cyan\" \"tsc -p tsconfig.build.json --watch --preserveWatchOutput\" \"tailwindcss -i ./src/app.css -o ./dist/app.css --watch\"",
|
|
17
|
+
"check": "tsc --noEmit && cd example && tsc --noEmit"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@
|
|
21
|
-
"@mariozechner/pi-
|
|
20
|
+
"@lmstudio/sdk": "^1.5.0",
|
|
21
|
+
"@mariozechner/pi-ai": "^0.6.0",
|
|
22
|
+
"@mariozechner/pi-tui": "^0.7.1",
|
|
22
23
|
"docx-preview": "^0.3.7",
|
|
23
24
|
"jszip": "^3.10.1",
|
|
24
|
-
"lit": "^3.3.1",
|
|
25
25
|
"lucide": "^0.544.0",
|
|
26
26
|
"ollama": "^0.6.0",
|
|
27
27
|
"pdfjs-dist": "^5.4.296",
|
|
28
28
|
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"@mariozechner/mini-lit": "^0.
|
|
31
|
+
"@mariozechner/mini-lit": "^0.2.0",
|
|
32
|
+
"lit": "^3.3.1"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
|
-
"@mariozechner/mini-lit": "^0.
|
|
35
|
+
"@mariozechner/mini-lit": "^0.2.0",
|
|
35
36
|
"@tailwindcss/cli": "^4.0.0-beta.14",
|
|
36
37
|
"concurrently": "^9.2.1",
|
|
37
38
|
"typescript": "^5.7.3"
|
package/src/ChatPanel.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Badge
|
|
2
|
-
import { LitElement } from "lit";
|
|
1
|
+
import { Badge } from "@mariozechner/mini-lit/dist/Badge.js";
|
|
2
|
+
import { html, LitElement } from "lit";
|
|
3
3
|
import { customElement, state } from "lit/decorators.js";
|
|
4
4
|
import type { Agent } from "./agent/agent.js";
|
|
5
5
|
import "./components/AgentInterface.js";
|
|
@@ -59,6 +59,7 @@ export class ChatPanel extends LitElement {
|
|
|
59
59
|
config?: {
|
|
60
60
|
onApiKeyRequired?: (provider: string) => Promise<boolean>;
|
|
61
61
|
onBeforeSend?: () => void | Promise<void>;
|
|
62
|
+
onCostClick?: () => void;
|
|
62
63
|
sandboxUrlProvider?: () => string;
|
|
63
64
|
toolsFactory?: (
|
|
64
65
|
agent: Agent,
|
|
@@ -79,6 +80,7 @@ export class ChatPanel extends LitElement {
|
|
|
79
80
|
this.agentInterface.showThemeToggle = false;
|
|
80
81
|
this.agentInterface.onApiKeyRequired = config?.onApiKeyRequired;
|
|
81
82
|
this.agentInterface.onBeforeSend = config?.onBeforeSend;
|
|
83
|
+
this.agentInterface.onCostClick = config?.onCostClick;
|
|
82
84
|
|
|
83
85
|
// Set up artifacts panel
|
|
84
86
|
this.artifactsPanel = new ArtifactsPanel();
|
|
@@ -6,11 +6,12 @@ import {
|
|
|
6
6
|
type UserMessage,
|
|
7
7
|
} from "@mariozechner/pi-ai";
|
|
8
8
|
import { getAppStorage } from "../../storage/app-storage.js";
|
|
9
|
+
import { applyProxyIfNeeded } from "../../utils/proxy-utils.js";
|
|
9
10
|
import type { AgentRunConfig, AgentTransport } from "./types.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Transport that calls LLM providers directly.
|
|
13
|
-
*
|
|
14
|
+
* Uses CORS proxy only for providers that require it (Anthropic OAuth, Z-AI).
|
|
14
15
|
*/
|
|
15
16
|
export class ProviderTransport implements AgentTransport {
|
|
16
17
|
async *run(messages: Message[], userMessage: Message, cfg: AgentRunConfig, signal?: AbortSignal) {
|
|
@@ -20,18 +21,12 @@ export class ProviderTransport implements AgentTransport {
|
|
|
20
21
|
throw new Error("no-api-key");
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
//
|
|
24
|
+
// Get proxy URL from settings (if available)
|
|
24
25
|
const proxyEnabled = await getAppStorage().settings.get<boolean>("proxy.enabled");
|
|
25
26
|
const proxyUrl = await getAppStorage().settings.get<string>("proxy.url");
|
|
26
27
|
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
if (proxyEnabled && proxyUrl && cfg.model.baseUrl) {
|
|
30
|
-
model = {
|
|
31
|
-
...cfg.model,
|
|
32
|
-
baseUrl: `${proxyUrl}/?url=${encodeURIComponent(cfg.model.baseUrl)}`,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
28
|
+
// Apply proxy only if this provider/key combination requires it
|
|
29
|
+
const model = applyProxyIfNeeded(cfg.model, apiKey, proxyEnabled ? proxyUrl || undefined : undefined);
|
|
35
30
|
|
|
36
31
|
// Messages are already LLM-compatible (filtered by Agent)
|
|
37
32
|
const context: AgentContext = {
|
package/src/app.css
CHANGED
|
@@ -42,3 +42,27 @@ body {
|
|
|
42
42
|
.fixed.inset-0 button[type="button"] {
|
|
43
43
|
cursor: pointer;
|
|
44
44
|
}
|
|
45
|
+
|
|
46
|
+
/* Shimmer animation for thinking text */
|
|
47
|
+
@keyframes shimmer {
|
|
48
|
+
0% {
|
|
49
|
+
background-position: -200% 0;
|
|
50
|
+
}
|
|
51
|
+
100% {
|
|
52
|
+
background-position: 200% 0;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.animate-shimmer {
|
|
57
|
+
animation: shimmer 2s ease-in-out infinite;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* User message with fancy pill styling */
|
|
61
|
+
.user-message-container {
|
|
62
|
+
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
|
|
63
|
+
position: relative;
|
|
64
|
+
background: linear-gradient(135deg, rgba(217, 79, 0, 0.12), rgba(255, 107, 0, 0.12), rgba(212, 165, 0, 0.12));
|
|
65
|
+
border: 1px solid rgba(255, 107, 0, 0.25);
|
|
66
|
+
backdrop-filter: blur(10px);
|
|
67
|
+
max-width: 100%;
|
|
68
|
+
}
|