Skip to content

Commit 74166e7

Browse files
metiu1claude
andcommitted
Attached-file path to agent, more cloud providers, 16kHz voice
- Attached files now pass their REAL server path into the prompt so the agent reads them with read_file instead of inventing /path/to/file. - Added cloud providers: xAI (Grok), Together AI, DeepSeek, Perplexity (OpenAI-compatible) with preset models — alongside OpenAI/Anthropic/ Gemini/Groq/Mistral/OpenRouter. - Voice: resample the recording to 16 kHz mono (what Whisper expects) and warn on empty/silent capture — fixes 'records but transcribes nothing'. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 2a42b97 commit 74166e7

3 files changed

Lines changed: 78 additions & 3 deletions

File tree

vortelio/internal/cloud/cloud.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,46 @@ var Providers = []Provider{
9696
Format: FormatOpenAI,
9797
KeyHint: "https://openrouter.ai/keys",
9898
},
99+
{
100+
ID: "xai",
101+
Name: "xAI (Grok)",
102+
DefaultModel: "grok-2-latest",
103+
BaseURL: "https://api.x.ai/v1/chat/completions",
104+
AuthHeader: "Authorization",
105+
AuthPrefix: "Bearer ",
106+
Format: FormatOpenAI,
107+
KeyHint: "https://console.x.ai",
108+
},
109+
{
110+
ID: "together",
111+
Name: "Together AI",
112+
DefaultModel: "meta-llama/Llama-3.3-70B-Instruct-Turbo",
113+
BaseURL: "https://api.together.xyz/v1/chat/completions",
114+
AuthHeader: "Authorization",
115+
AuthPrefix: "Bearer ",
116+
Format: FormatOpenAI,
117+
KeyHint: "https://api.together.xyz/settings/api-keys",
118+
},
119+
{
120+
ID: "deepseek",
121+
Name: "DeepSeek",
122+
DefaultModel: "deepseek-chat",
123+
BaseURL: "https://api.deepseek.com/v1/chat/completions",
124+
AuthHeader: "Authorization",
125+
AuthPrefix: "Bearer ",
126+
Format: FormatOpenAI,
127+
KeyHint: "https://platform.deepseek.com/api_keys",
128+
},
129+
{
130+
ID: "perplexity",
131+
Name: "Perplexity",
132+
DefaultModel: "sonar",
133+
BaseURL: "https://api.perplexity.ai/chat/completions",
134+
AuthHeader: "Authorization",
135+
AuthPrefix: "Bearer ",
136+
Format: FormatOpenAI,
137+
KeyHint: "https://www.perplexity.ai/settings/api",
138+
},
99139
}
100140

101141
func FindProvider(id string) (Provider, bool) {

vortelio/internal/server/server_cloud.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,25 @@ var cloudModelChoices = map[string][][2]string{
4848
{"openai/gpt-4o", "GPT-4o"},
4949
{"deepseek/deepseek-r1", "DeepSeek R1"},
5050
},
51+
"xai": {
52+
{"grok-2-latest", "Grok 2"},
53+
{"grok-2-vision-latest", "Grok 2 Vision"},
54+
{"grok-beta", "Grok Beta"},
55+
},
56+
"together": {
57+
{"meta-llama/Llama-3.3-70B-Instruct-Turbo", "Llama 3.3 70B Turbo"},
58+
{"Qwen/Qwen2.5-72B-Instruct-Turbo", "Qwen2.5 72B Turbo"},
59+
{"mistralai/Mixtral-8x7B-Instruct-v0.1", "Mixtral 8x7B"},
60+
},
61+
"deepseek": {
62+
{"deepseek-chat", "DeepSeek V3 (chat)"},
63+
{"deepseek-reasoner", "DeepSeek R1 (reasoner)"},
64+
},
65+
"perplexity": {
66+
{"sonar", "Sonar"},
67+
{"sonar-pro", "Sonar Pro"},
68+
{"sonar-reasoning", "Sonar Reasoning"},
69+
},
5170
}
5271

5372
// GET /api/cloud/providers

vortelio/internal/server/ui.html

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4219,10 +4219,19 @@ <h3 style="margin:0">☁️ Add cloud model</h3>
42194219
toast('⏳ Trascrivo…');
42204220
try {
42214221
const blob = new Blob(_recChunks, { type: _recMedia.mimeType || 'audio/webm' });
4222+
if (!blob.size) { toast('🎤 Nessun audio registrato — controlla il microfono e riprova.', 'err'); return; }
42224223
const ab = await blob.arrayBuffer();
42234224
const ctx = new (window.AudioContext || window.webkitAudioContext)();
42244225
const audio = await ctx.decodeAudioData(ab);
4225-
const wav = _encodeWAV(audio);
4226+
// Resample to 16 kHz mono — exactly what Whisper expects (avoids empty results).
4227+
const off = new OfflineAudioContext(1, Math.max(1, Math.ceil(audio.duration * 16000)), 16000);
4228+
const node = off.createBufferSource(); node.buffer = audio; node.connect(off.destination); node.start();
4229+
const rendered = await off.startRendering();
4230+
// Bail out if the mic captured basically silence.
4231+
const pcm = rendered.getChannelData(0); let peak = 0;
4232+
for (let i = 0; i < pcm.length; i += 64) { const a = Math.abs(pcm[i]); if (a > peak) peak = a; }
4233+
if (peak < 0.005) { toast('🎤 Audio troppo silenzioso — parla più vicino al microfono.', 'err'); return; }
4234+
const wav = _encodeWAV(rendered);
42264235
const fd = new FormData();
42274236
fd.append('file', wav, 'rec.wav');
42284237
fd.append('model', audioModel.type + '/' + audioModel.name + ':' + audioModel.tag);
@@ -4609,9 +4618,16 @@ <h3 style="margin:0">☁️ Add cloud model</h3>
46094618
}
46104619

46114620
try {
4621+
// If files are attached, tell the model their REAL paths so its file tools
4622+
// can read them (otherwise it invents a placeholder like /path/to/file).
4623+
let promptText = text;
4624+
const attachPaths = (files || []).map(f => f.serverPath).filter(Boolean);
4625+
if (attachPaths.length) {
4626+
promptText += '\n\n[File allegati dall\'utente — leggine il contenuto con lo strumento read_file usando ESATTAMENTE questo percorso: ' + attachPaths.join(' | ') + ']';
4627+
}
46124628
// Send full history so the LLM has conversation context
46134629
const payload = {
4614-
model: selectedModel, prompt: text, stream: true,
4630+
model: selectedModel, prompt: promptText, stream: true,
46154631
force_cpu: useCPU, context_size: contextSize,
46164632
think: thinkOn, // enable chain-of-thought for reasoning models
46174633
tools_enabled: toolsOn, // enable function calling / tool use
@@ -4620,7 +4636,7 @@ <h3 style="margin:0">☁️ Add cloud model</h3>
46204636
};
46214637
const ag = buildAgenticPayload();
46224638
if (ag) payload.agentic = ag;
4623-
if (files.length) payload.input_file = files[0].serverPath;
4639+
if (attachPaths.length) payload.input_file = attachPaths[0];
46244640
const r = await fetchRetry('/api/generate', {method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(payload), signal});
46254641

46264642
pulse.remove();

0 commit comments

Comments
 (0)