@astra-code/astra-ai 0.1.8 → 0.1.9
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/package.json +1 -1
- package/src/lib/voice.ts +54 -8
package/package.json
CHANGED
package/src/lib/voice.ts
CHANGED
|
@@ -5,7 +5,8 @@ import {basename, join} from "path";
|
|
|
5
5
|
import {tmpdir} from "os";
|
|
6
6
|
import {readFile, rm} from "fs/promises";
|
|
7
7
|
import {getBackendUrl} from "./config.js";
|
|
8
|
-
import {loadSession} from "./sessionStore.js";
|
|
8
|
+
import {loadSession, saveSession} from "./sessionStore.js";
|
|
9
|
+
import type {AuthSession} from "../types/events.js";
|
|
9
10
|
|
|
10
11
|
const VOICE_TEXT_LIMIT = 600;
|
|
11
12
|
const DEFAULT_STT_MODEL = process.env.ASTRA_STT_MODEL?.trim() || "whisper-1";
|
|
@@ -169,6 +170,31 @@ const captureAudioChunk = async (seconds: number): Promise<string> => {
|
|
|
169
170
|
return outPath;
|
|
170
171
|
};
|
|
171
172
|
|
|
173
|
+
const tryRefreshToken = async (): Promise<string | null> => {
|
|
174
|
+
const stored = loadSession();
|
|
175
|
+
const refreshToken = stored?.refresh_token?.trim();
|
|
176
|
+
if (!refreshToken) return null;
|
|
177
|
+
try {
|
|
178
|
+
const res = await fetch(`${getBackendUrl()}/api/auth/refresh`, {
|
|
179
|
+
method: "POST",
|
|
180
|
+
headers: {"content-type": "application/json"},
|
|
181
|
+
body: JSON.stringify({refresh_token: refreshToken})
|
|
182
|
+
});
|
|
183
|
+
if (!res.ok) return null;
|
|
184
|
+
const data = (await res.json()) as Record<string, unknown>;
|
|
185
|
+
const newAccess = typeof data.access_token === "string" ? data.access_token.trim() : null;
|
|
186
|
+
if (!newAccess) return null;
|
|
187
|
+
const newRefresh = typeof data.refresh_token === "string" ? data.refresh_token.trim() : refreshToken;
|
|
188
|
+
if (stored) {
|
|
189
|
+
const refreshed: AuthSession = {...stored, access_token: newAccess, refresh_token: newRefresh};
|
|
190
|
+
saveSession(refreshed);
|
|
191
|
+
}
|
|
192
|
+
return newAccess;
|
|
193
|
+
} catch {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
172
198
|
const transcribeAudioFile = async (filePath: string): Promise<string> => {
|
|
173
199
|
const bytes = await readFile(filePath);
|
|
174
200
|
// #region agent log
|
|
@@ -176,25 +202,45 @@ const transcribeAudioFile = async (filePath: string): Promise<string> => {
|
|
|
176
202
|
// #endregion
|
|
177
203
|
const file = new File([bytes], basename(filePath), {type: "audio/wav"});
|
|
178
204
|
|
|
179
|
-
// Backend proxy only: backend holds provider secrets.
|
|
180
|
-
const token = loadSession()?.access_token;
|
|
181
|
-
if (!token) {
|
|
182
|
-
throw new Error("Missing auth session for backend STT. Please sign in again.");
|
|
183
|
-
}
|
|
184
205
|
const form = new FormData();
|
|
185
206
|
form.append("file", file);
|
|
186
207
|
form.append("model", DEFAULT_STT_MODEL);
|
|
187
|
-
|
|
208
|
+
|
|
209
|
+
let token = loadSession()?.access_token?.trim();
|
|
210
|
+
if (!token) {
|
|
211
|
+
throw new Error("Missing auth session for backend STT. Please sign in again.");
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
let response = await fetch(`${getBackendUrl()}/api/agent/transcribe`, {
|
|
188
215
|
method: "POST",
|
|
189
216
|
headers: {Authorization: `Bearer ${token}`},
|
|
190
217
|
body: form
|
|
191
218
|
});
|
|
219
|
+
|
|
220
|
+
if (response.status === 401) {
|
|
221
|
+
const newToken = await tryRefreshToken();
|
|
222
|
+
if (newToken) {
|
|
223
|
+
const formRetry = new FormData();
|
|
224
|
+
formRetry.append("file", new File([bytes], basename(filePath), {type: "audio/wav"}));
|
|
225
|
+
formRetry.append("model", DEFAULT_STT_MODEL);
|
|
226
|
+
response = await fetch(`${getBackendUrl()}/api/agent/transcribe`, {
|
|
227
|
+
method: "POST",
|
|
228
|
+
headers: {Authorization: `Bearer ${newToken}`},
|
|
229
|
+
body: formRetry
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
192
234
|
// #region agent log
|
|
193
235
|
fetch('http://127.0.0.1:7573/ingest/fdd4f018-1ba3-4303-b1bb-375443267476',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'17f1ea'},body:JSON.stringify({sessionId:'17f1ea',runId:'voice_run_2',hypothesisId:'H7',location:'voice.ts:transcribeAudioFile',message:'backend transcribe response',data:{status:response.status,ok:response.ok},timestamp:Date.now()})}).catch(()=>{});
|
|
194
236
|
// #endregion
|
|
195
237
|
if (!response.ok) {
|
|
196
238
|
const detail = (await response.text()).slice(0, 400);
|
|
197
|
-
|
|
239
|
+
const hint =
|
|
240
|
+
response.status === 401
|
|
241
|
+
? " Token expired and refresh failed. Run /logout and sign in again."
|
|
242
|
+
: "";
|
|
243
|
+
throw new Error(`Backend transcription failed ${response.status}: ${detail}${hint}`);
|
|
198
244
|
}
|
|
199
245
|
const data = (await response.json()) as {text?: string};
|
|
200
246
|
const out = String(data.text ?? "").trim();
|