@steipete/summarize 0.1.2 → 0.3.0

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.
Files changed (54) hide show
  1. package/CHANGELOG.md +66 -3
  2. package/README.md +40 -6
  3. package/dist/cli.cjs +6502 -634
  4. package/dist/cli.cjs.map +4 -4
  5. package/dist/esm/content/asset.js +18 -0
  6. package/dist/esm/content/asset.js.map +1 -1
  7. package/dist/esm/content/link-preview/client.js +8 -0
  8. package/dist/esm/content/link-preview/client.js.map +1 -1
  9. package/dist/esm/content/link-preview/content/article.js +15 -1
  10. package/dist/esm/content/link-preview/content/article.js.map +1 -1
  11. package/dist/esm/content/link-preview/content/index.js +151 -4
  12. package/dist/esm/content/link-preview/content/index.js.map +1 -1
  13. package/dist/esm/content/link-preview/transcript/index.js +6 -0
  14. package/dist/esm/content/link-preview/transcript/index.js.map +1 -1
  15. package/dist/esm/content/link-preview/transcript/providers/youtube/yt-dlp.js +213 -0
  16. package/dist/esm/content/link-preview/transcript/providers/youtube/yt-dlp.js.map +1 -0
  17. package/dist/esm/content/link-preview/transcript/providers/youtube.js +40 -2
  18. package/dist/esm/content/link-preview/transcript/providers/youtube.js.map +1 -1
  19. package/dist/esm/flags.js +14 -2
  20. package/dist/esm/flags.js.map +1 -1
  21. package/dist/esm/llm/generate-text.js +125 -21
  22. package/dist/esm/llm/generate-text.js.map +1 -1
  23. package/dist/esm/llm/html-to-markdown.js +3 -2
  24. package/dist/esm/llm/html-to-markdown.js.map +1 -1
  25. package/dist/esm/pricing/litellm.js +4 -1
  26. package/dist/esm/pricing/litellm.js.map +1 -1
  27. package/dist/esm/prompts/file.js +15 -4
  28. package/dist/esm/prompts/file.js.map +1 -1
  29. package/dist/esm/prompts/link-summary.js +20 -6
  30. package/dist/esm/prompts/link-summary.js.map +1 -1
  31. package/dist/esm/run.js +545 -407
  32. package/dist/esm/run.js.map +1 -1
  33. package/dist/esm/version.js +1 -1
  34. package/dist/types/content/link-preview/client.d.ts +5 -1
  35. package/dist/types/content/link-preview/content/types.d.ts +1 -1
  36. package/dist/types/content/link-preview/deps.d.ts +33 -0
  37. package/dist/types/content/link-preview/transcript/providers/youtube/yt-dlp.d.ts +15 -0
  38. package/dist/types/content/link-preview/transcript/types.d.ts +4 -0
  39. package/dist/types/content/link-preview/types.d.ts +1 -1
  40. package/dist/types/costs.d.ts +1 -1
  41. package/dist/types/flags.d.ts +1 -1
  42. package/dist/types/llm/generate-text.d.ts +8 -2
  43. package/dist/types/llm/html-to-markdown.d.ts +4 -1
  44. package/dist/types/pricing/litellm.d.ts +1 -0
  45. package/dist/types/prompts/file.d.ts +2 -1
  46. package/dist/types/version.d.ts +1 -1
  47. package/docs/extract-only.md +1 -1
  48. package/docs/firecrawl.md +2 -2
  49. package/docs/llm.md +7 -0
  50. package/docs/site/docs/config.html +1 -1
  51. package/docs/site/docs/firecrawl.html +1 -1
  52. package/docs/website.md +3 -3
  53. package/docs/youtube.md +5 -2
  54. package/package.json +7 -2
@@ -0,0 +1,213 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { promises as fs } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ import { createFalClient } from '@fal-ai/client';
7
+ const YT_DLP_TIMEOUT_MS = 300_000;
8
+ const TRANSCRIPTION_TIMEOUT_MS = 600_000;
9
+ const MAX_STDERR_BYTES = 8192;
10
+ export const fetchTranscriptWithYtDlp = async ({ ytDlpPath, openaiApiKey, falApiKey, url, }) => {
11
+ const notes = [];
12
+ if (!ytDlpPath) {
13
+ return { text: null, provider: null, error: new Error('YT_DLP_PATH is not configured'), notes };
14
+ }
15
+ if (!openaiApiKey && !falApiKey) {
16
+ return {
17
+ text: null,
18
+ provider: null,
19
+ error: new Error('OPENAI_API_KEY or FAL_KEY is required for yt-dlp transcription'),
20
+ notes,
21
+ };
22
+ }
23
+ const outputFile = join(tmpdir(), `summarize-${randomUUID()}.mp3`);
24
+ let audioBytes;
25
+ try {
26
+ await downloadAudio(ytDlpPath, url, outputFile);
27
+ audioBytes = await fs.readFile(outputFile);
28
+ }
29
+ catch (error) {
30
+ return {
31
+ text: null,
32
+ provider: null,
33
+ error: wrapError('yt-dlp failed to download audio', error),
34
+ notes,
35
+ };
36
+ }
37
+ finally {
38
+ await fs.unlink(outputFile).catch(() => { });
39
+ }
40
+ let openaiError = null;
41
+ if (openaiApiKey) {
42
+ try {
43
+ const text = await transcribeWithOpenAi(toArrayBuffer(audioBytes), openaiApiKey);
44
+ if (text) {
45
+ return { text, provider: 'openai', error: null, notes };
46
+ }
47
+ openaiError = new Error('OpenAI transcription returned empty text');
48
+ }
49
+ catch (error) {
50
+ openaiError = wrapError('OpenAI transcription failed', error);
51
+ }
52
+ }
53
+ if (openaiError && falApiKey) {
54
+ notes.push(`OpenAI transcription failed; falling back to FAL: ${openaiError.message}`);
55
+ }
56
+ if (falApiKey) {
57
+ try {
58
+ const text = await transcribeWithFal(toArrayBuffer(audioBytes), falApiKey);
59
+ if (text) {
60
+ return { text, provider: 'fal', error: null, notes };
61
+ }
62
+ return {
63
+ text: null,
64
+ provider: 'fal',
65
+ error: new Error('FAL transcription returned empty text'),
66
+ notes,
67
+ };
68
+ }
69
+ catch (error) {
70
+ return {
71
+ text: null,
72
+ provider: 'fal',
73
+ error: wrapError('FAL transcription failed', error),
74
+ notes,
75
+ };
76
+ }
77
+ }
78
+ return {
79
+ text: null,
80
+ provider: openaiApiKey ? 'openai' : null,
81
+ error: openaiError ?? new Error('No transcription providers available'),
82
+ notes,
83
+ };
84
+ };
85
+ async function transcribeWithOpenAi(audioBytes, apiKey) {
86
+ const form = new FormData();
87
+ form.append('file', new Blob([audioBytes], { type: 'audio/mpeg' }), 'audio.mp3');
88
+ form.append('model', 'whisper-1');
89
+ const response = await fetch('https://api.openai.com/v1/audio/transcriptions', {
90
+ method: 'POST',
91
+ headers: { Authorization: `Bearer ${apiKey}` },
92
+ body: form,
93
+ signal: AbortSignal.timeout(TRANSCRIPTION_TIMEOUT_MS),
94
+ });
95
+ if (!response.ok) {
96
+ const detail = await readErrorDetail(response);
97
+ const suffix = detail ? `: ${detail}` : '';
98
+ throw new Error(`OpenAI transcription failed (${response.status})${suffix}`);
99
+ }
100
+ const payload = (await response.json());
101
+ if (typeof payload?.text !== 'string')
102
+ return null;
103
+ const trimmed = payload.text.trim();
104
+ return trimmed.length > 0 ? trimmed : null;
105
+ }
106
+ async function transcribeWithFal(audioBytes, apiKey) {
107
+ const fal = createFalClient({ credentials: apiKey });
108
+ const audioUrl = await fal.storage.upload(new Blob([audioBytes], { type: 'audio/mpeg' }));
109
+ const result = await Promise.race([
110
+ fal.subscribe('fal-ai/wizper', {
111
+ input: { audio_url: audioUrl, language: 'en' },
112
+ }),
113
+ new Promise((_, reject) => setTimeout(() => reject(new Error('FAL transcription timeout')), TRANSCRIPTION_TIMEOUT_MS)),
114
+ ]);
115
+ return extractText(result);
116
+ }
117
+ async function downloadAudio(ytDlpPath, url, outputFile) {
118
+ return new Promise((resolve, reject) => {
119
+ const args = [
120
+ '-x',
121
+ '--audio-format',
122
+ 'mp3',
123
+ '--no-playlist',
124
+ '--retries',
125
+ '3',
126
+ '--no-warnings',
127
+ '-o',
128
+ outputFile,
129
+ url,
130
+ ];
131
+ const proc = spawn(ytDlpPath, args, { stdio: ['ignore', 'ignore', 'pipe'] });
132
+ let stderr = '';
133
+ if (proc.stderr) {
134
+ proc.stderr.setEncoding('utf8');
135
+ proc.stderr.on('data', (chunk) => {
136
+ if (stderr.length >= MAX_STDERR_BYTES)
137
+ return;
138
+ const remaining = MAX_STDERR_BYTES - stderr.length;
139
+ stderr += chunk.slice(0, remaining);
140
+ });
141
+ }
142
+ const timeout = setTimeout(() => {
143
+ proc.kill('SIGTERM');
144
+ reject(new Error('yt-dlp download timeout'));
145
+ }, YT_DLP_TIMEOUT_MS);
146
+ proc.on('close', (code, signal) => {
147
+ clearTimeout(timeout);
148
+ if (code === 0) {
149
+ resolve();
150
+ return;
151
+ }
152
+ const detail = stderr.trim();
153
+ const suffix = detail ? `: ${detail}` : '';
154
+ if (code === null) {
155
+ reject(new Error(`yt-dlp terminated (${signal ?? 'unknown'})${suffix}`));
156
+ return;
157
+ }
158
+ reject(new Error(`yt-dlp exited with code ${code}${suffix}`));
159
+ });
160
+ proc.on('error', (error) => {
161
+ clearTimeout(timeout);
162
+ reject(error);
163
+ });
164
+ });
165
+ }
166
+ function extractText(result) {
167
+ if (typeof result !== 'object' || result === null)
168
+ return null;
169
+ const data = 'data' in result ? result.data : result;
170
+ if (typeof data !== 'object' || data === null)
171
+ return null;
172
+ if ('text' in data && typeof data.text === 'string') {
173
+ const text = data.text.trim();
174
+ return text.length > 0 ? text : null;
175
+ }
176
+ if ('chunks' in data && Array.isArray(data.chunks)) {
177
+ const chunks = data.chunks;
178
+ const lines = [];
179
+ for (const chunk of chunks) {
180
+ if (typeof chunk === 'object' && chunk !== null && 'text' in chunk) {
181
+ const text = chunk.text;
182
+ if (typeof text === 'string' && text.trim()) {
183
+ lines.push(text.trim());
184
+ }
185
+ }
186
+ }
187
+ return lines.length > 0 ? lines.join(' ') : null;
188
+ }
189
+ return null;
190
+ }
191
+ async function readErrorDetail(response) {
192
+ try {
193
+ const text = await response.text();
194
+ const trimmed = text.trim();
195
+ if (!trimmed)
196
+ return null;
197
+ return trimmed.length > 200 ? `${trimmed.slice(0, 200)}…` : trimmed;
198
+ }
199
+ catch {
200
+ return null;
201
+ }
202
+ }
203
+ function wrapError(prefix, error) {
204
+ if (error instanceof Error) {
205
+ return new Error(`${prefix}: ${error.message}`, { cause: error });
206
+ }
207
+ return new Error(`${prefix}: ${String(error)}`);
208
+ }
209
+ function toArrayBuffer(view) {
210
+ const buffer = view.buffer;
211
+ return buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);
212
+ }
213
+ //# sourceMappingURL=yt-dlp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yt-dlp.js","sourceRoot":"","sources":["../../../../../../../src/content/link-preview/transcript/providers/youtube/yt-dlp.ts"],"names":[],"mappings":"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"}
@@ -3,12 +3,22 @@ import { extractYouTubeVideoId } from '../utils.js';
3
3
  import { extractYoutubeiTranscriptConfig, fetchTranscriptFromTranscriptEndpoint, } from './youtube/api.js';
4
4
  import { fetchTranscriptWithApify } from './youtube/apify.js';
5
5
  import { fetchTranscriptFromCaptionTracks } from './youtube/captions.js';
6
+ import { fetchTranscriptWithYtDlp } from './youtube/yt-dlp.js';
6
7
  const YOUTUBE_URL_PATTERN = /youtube\.com|youtu\.be/i;
7
8
  export const canHandle = ({ url }) => YOUTUBE_URL_PATTERN.test(url);
8
9
  export const fetchTranscript = async (context, options) => {
9
10
  const attemptedProviders = [];
11
+ const notes = [];
10
12
  const { html, url } = context;
11
13
  const mode = options.youtubeTranscriptMode;
14
+ const hasYtDlpCredentials = Boolean(options.openaiApiKey || options.falApiKey);
15
+ const canRunYtDlp = Boolean(options.ytDlpPath && hasYtDlpCredentials);
16
+ if (mode === 'yt-dlp' && !options.ytDlpPath) {
17
+ throw new Error('Missing YT_DLP_PATH for --youtube yt-dlp');
18
+ }
19
+ if (mode === 'yt-dlp' && !hasYtDlpCredentials) {
20
+ throw new Error('Missing OPENAI_API_KEY or FAL_KEY for --youtube yt-dlp');
21
+ }
12
22
  if (!html) {
13
23
  return { text: null, source: null, attemptedProviders };
14
24
  }
@@ -19,7 +29,8 @@ export const fetchTranscript = async (context, options) => {
19
29
  if (!effectiveVideoId) {
20
30
  return { text: null, source: null, attemptedProviders };
21
31
  }
22
- if (mode !== 'apify') {
32
+ // Try web methods (youtubei, captionTracks) if mode is 'auto' or 'web'
33
+ if (mode === 'auto' || mode === 'web') {
23
34
  const config = extractYoutubeiTranscriptConfig(html);
24
35
  if (config) {
25
36
  attemptedProviders.push('youtubei');
@@ -51,7 +62,8 @@ export const fetchTranscript = async (context, options) => {
51
62
  };
52
63
  }
53
64
  }
54
- if (mode !== 'web') {
65
+ // Try apify if mode is 'auto' or 'apify'
66
+ if (mode === 'auto' || mode === 'apify') {
55
67
  attemptedProviders.push('apify');
56
68
  const apifyTranscript = await fetchTranscriptWithApify(options.fetch, options.apifyApiToken, url);
57
69
  if (apifyTranscript) {
@@ -63,12 +75,38 @@ export const fetchTranscript = async (context, options) => {
63
75
  };
64
76
  }
65
77
  }
78
+ // Try yt-dlp (audio download + OpenAI/FAL transcription) if mode is 'auto' or 'yt-dlp'
79
+ if (mode === 'yt-dlp' || (mode === 'auto' && canRunYtDlp)) {
80
+ attemptedProviders.push('yt-dlp');
81
+ const ytdlpResult = await fetchTranscriptWithYtDlp({
82
+ ytDlpPath: options.ytDlpPath,
83
+ openaiApiKey: options.openaiApiKey,
84
+ falApiKey: options.falApiKey,
85
+ url,
86
+ });
87
+ if (ytdlpResult.notes.length > 0) {
88
+ notes.push(...ytdlpResult.notes);
89
+ }
90
+ if (ytdlpResult.text) {
91
+ return {
92
+ text: normalizeTranscriptText(ytdlpResult.text),
93
+ source: 'yt-dlp',
94
+ metadata: { provider: 'yt-dlp', transcriptionProvider: ytdlpResult.provider },
95
+ attemptedProviders,
96
+ notes: notes.length > 0 ? notes.join('; ') : null,
97
+ };
98
+ }
99
+ if (mode === 'yt-dlp' && ytdlpResult.error) {
100
+ throw ytdlpResult.error;
101
+ }
102
+ }
66
103
  attemptedProviders.push('unavailable');
67
104
  return {
68
105
  text: null,
69
106
  source: 'unavailable',
70
107
  metadata: { provider: 'youtube', reason: 'no_transcript_available' },
71
108
  attemptedProviders,
109
+ notes: notes.length > 0 ? notes.join('; ') : null,
72
110
  };
73
111
  };
74
112
  //# sourceMappingURL=youtube.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"youtube.js","sourceRoot":"","sources":["../../../../../../src/content/link-preview/transcript/providers/youtube.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"youtube.js","sourceRoot":"","sources":["../../../../../../src/content/link-preview/transcript/providers/youtube.ts"],"names":[],"mappings":"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"}
package/dist/esm/flags.js CHANGED
@@ -1,12 +1,16 @@
1
1
  const SUMMARY_LENGTHS = ['short', 'medium', 'long', 'xl', 'xxl'];
2
2
  const DURATION_PATTERN = /^(?<value>\d+(?:\.\d+)?)(?<unit>ms|s|m|h)?$/i;
3
3
  const COUNT_PATTERN = /^(?<value>\d+(?:\.\d+)?)(?<unit>k|m)?$/i;
4
+ const MIN_LENGTH_CHARS = 50;
5
+ const MIN_MAX_OUTPUT_TOKENS = 16;
4
6
  export function parseYoutubeMode(raw) {
5
7
  const normalized = raw.trim().toLowerCase();
6
8
  if (normalized === 'autp')
7
9
  return 'auto';
8
10
  if (normalized === 'auto' || normalized === 'web' || normalized === 'apify')
9
11
  return normalized;
12
+ if (normalized === 'yt-dlp')
13
+ return 'yt-dlp';
10
14
  throw new Error(`Unsupported --youtube: ${raw}`);
11
15
  }
12
16
  export function parseFirecrawlMode(raw) {
@@ -73,7 +77,11 @@ export function parseLengthArg(raw) {
73
77
  }
74
78
  const unit = match.groups.unit?.toLowerCase() ?? null;
75
79
  const multiplier = unit === 'k' ? 1000 : unit === 'm' ? 1_000_000 : 1;
76
- return { kind: 'chars', maxCharacters: Math.floor(numeric * multiplier) };
80
+ const maxCharacters = Math.floor(numeric * multiplier);
81
+ if (maxCharacters < MIN_LENGTH_CHARS) {
82
+ throw new Error(`Unsupported --length: ${raw} (minimum ${MIN_LENGTH_CHARS} chars)`);
83
+ }
84
+ return { kind: 'chars', maxCharacters };
77
85
  }
78
86
  export function parseMaxOutputTokensArg(raw) {
79
87
  if (raw === undefined || raw === null)
@@ -92,6 +100,10 @@ export function parseMaxOutputTokensArg(raw) {
92
100
  }
93
101
  const unit = match.groups.unit?.toLowerCase() ?? null;
94
102
  const multiplier = unit === 'k' ? 1000 : unit === 'm' ? 1_000_000 : 1;
95
- return Math.floor(numeric * multiplier);
103
+ const maxOutputTokens = Math.floor(numeric * multiplier);
104
+ if (maxOutputTokens < MIN_MAX_OUTPUT_TOKENS) {
105
+ throw new Error(`Unsupported --max-output-tokens: ${raw} (minimum ${MIN_MAX_OUTPUT_TOKENS})`);
106
+ }
107
+ return maxOutputTokens;
96
108
  }
97
109
  //# sourceMappingURL=flags.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"flags.js","sourceRoot":"","sources":["../../src/flags.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"flags.js","sourceRoot":"","sources":["../../src/flags.ts"],"names":[],"mappings":"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"}
@@ -61,7 +61,40 @@ function normalizeTokenUsage(raw) {
61
61
  }
62
62
  return { promptTokens, completionTokens, totalTokens };
63
63
  }
64
- export async function generateTextWithModelId({ modelId, apiKeys, system, prompt, temperature, maxOutputTokens, timeoutMs, fetchImpl, }) {
64
+ function resolveOpenAiClientConfig({ apiKeys, openrouter, fetchImpl, }) {
65
+ const baseUrlRaw = typeof process !== 'undefined' ? process.env.OPENAI_BASE_URL : undefined;
66
+ const baseUrl = typeof baseUrlRaw === 'string' && baseUrlRaw.trim().length > 0 ? baseUrlRaw.trim() : null;
67
+ const isOpenRouterViaBaseUrl = baseUrl ? /openrouter\.ai/i.test(baseUrl) : false;
68
+ const hasOpenRouterKey = apiKeys.openrouterApiKey != null;
69
+ const isOpenRouter = isOpenRouterViaBaseUrl || (hasOpenRouterKey && !baseUrl);
70
+ const apiKey = isOpenRouter
71
+ ? (apiKeys.openrouterApiKey ?? apiKeys.openaiApiKey)
72
+ : apiKeys.openaiApiKey;
73
+ if (!apiKey) {
74
+ throw new Error(isOpenRouter
75
+ ? 'Missing OPENROUTER_API_KEY (or OPENAI_API_KEY) for OpenRouter'
76
+ : 'Missing OPENAI_API_KEY for openai/... model');
77
+ }
78
+ const openrouterProviders = openrouter?.providers?.length ? openrouter.providers : null;
79
+ const wrappedFetch = isOpenRouter && openrouterProviders
80
+ ? (url, init) => {
81
+ const headers = new Headers(init?.headers);
82
+ headers.set('HTTP-Referer', 'https://github.com/steipete/summarize');
83
+ headers.set('X-Title', 'summarize');
84
+ headers.set('X-OpenRouter-Provider-Order', openrouterProviders.join(','));
85
+ return fetchImpl(url, { ...init, headers });
86
+ }
87
+ : fetchImpl;
88
+ const baseURL = baseUrl ?? (isOpenRouter ? 'https://openrouter.ai/api/v1' : undefined);
89
+ return {
90
+ apiKey,
91
+ baseURL: baseURL ?? undefined,
92
+ fetch: wrappedFetch,
93
+ useChatCompletions: isOpenRouter,
94
+ isOpenRouter,
95
+ };
96
+ }
97
+ export async function generateTextWithModelId({ modelId, apiKeys, system, prompt, temperature, maxOutputTokens, timeoutMs, fetchImpl, openrouter, }) {
65
98
  const parsed = parseGatewayStyleModelId(modelId);
66
99
  const controller = new AbortController();
67
100
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
@@ -130,13 +163,15 @@ export async function generateTextWithModelId({ modelId, apiKeys, system, prompt
130
163
  usage: normalizeTokenUsage(result.usage),
131
164
  };
132
165
  }
133
- const apiKey = apiKeys.openaiApiKey;
134
- if (!apiKey)
135
- throw new Error('Missing OPENAI_API_KEY for openai/... model');
136
166
  const { createOpenAI } = await import('@ai-sdk/openai');
137
- const openai = createOpenAI({ apiKey, fetch: fetchImpl });
138
- const baseUrl = typeof process !== 'undefined' ? process.env.OPENAI_BASE_URL : undefined;
139
- const useChatCompletions = typeof baseUrl === 'string' && /openrouter\.ai/i.test(baseUrl) && baseUrl.length > 0;
167
+ const openaiConfig = resolveOpenAiClientConfig({ apiKeys, openrouter, fetchImpl });
168
+ const openai = createOpenAI({
169
+ apiKey: openaiConfig.apiKey,
170
+ ...(openaiConfig.baseURL ? { baseURL: openaiConfig.baseURL } : {}),
171
+ fetch: openaiConfig.fetch,
172
+ });
173
+ // OpenRouter requires chat completions endpoint
174
+ const useChatCompletions = openaiConfig.useChatCompletions;
140
175
  const responsesModelId = parsed.model;
141
176
  const chatModelId = parsed.model;
142
177
  const result = await generateText({
@@ -169,13 +204,80 @@ export async function generateTextWithModelId({ modelId, apiKeys, system, prompt
169
204
  clearTimeout(timeout);
170
205
  }
171
206
  }
172
- export async function streamTextWithModelId({ modelId, apiKeys, system, prompt, temperature, maxOutputTokens, timeoutMs, fetchImpl, }) {
207
+ export async function streamTextWithModelId({ modelId, apiKeys, system, prompt, temperature, maxOutputTokens, timeoutMs, fetchImpl, openrouter, }) {
173
208
  const parsed = parseGatewayStyleModelId(modelId);
174
209
  const controller = new AbortController();
175
- const timeout = setTimeout(() => controller.abort(), timeoutMs);
210
+ let timeoutId = null;
211
+ const startedAtMs = Date.now();
212
+ let lastError = null;
213
+ const timeoutError = new Error('LLM request timed out');
214
+ const markTimedOut = () => {
215
+ if (lastError === timeoutError)
216
+ return;
217
+ lastError = timeoutError;
218
+ controller.abort();
219
+ };
220
+ const startTimeout = () => {
221
+ if (timeoutId)
222
+ return;
223
+ timeoutId = setTimeout(markTimedOut, timeoutMs);
224
+ };
225
+ const stopTimeout = () => {
226
+ if (!timeoutId)
227
+ return;
228
+ clearTimeout(timeoutId);
229
+ timeoutId = null;
230
+ };
231
+ const nextWithDeadline = async (promise) => {
232
+ const elapsed = Date.now() - startedAtMs;
233
+ const remaining = timeoutMs - elapsed;
234
+ if (remaining <= 0) {
235
+ markTimedOut();
236
+ throw timeoutError;
237
+ }
238
+ let timer = null;
239
+ try {
240
+ return await Promise.race([
241
+ promise,
242
+ new Promise((_, reject) => {
243
+ timer = setTimeout(() => {
244
+ markTimedOut();
245
+ reject(timeoutError);
246
+ }, remaining);
247
+ }),
248
+ ]);
249
+ }
250
+ finally {
251
+ if (timer)
252
+ clearTimeout(timer);
253
+ }
254
+ };
255
+ const wrapTextStream = (textStream) => ({
256
+ async *[Symbol.asyncIterator]() {
257
+ startTimeout();
258
+ const iterator = textStream[Symbol.asyncIterator]();
259
+ try {
260
+ while (true) {
261
+ const result = await nextWithDeadline(iterator.next());
262
+ if (result.done)
263
+ break;
264
+ yield result.value;
265
+ }
266
+ }
267
+ finally {
268
+ stopTimeout();
269
+ if (typeof iterator.return === 'function') {
270
+ const cleanup = iterator.return();
271
+ if (cleanup && typeof cleanup.catch === 'function') {
272
+ ;
273
+ cleanup.catch(() => { });
274
+ }
275
+ }
276
+ }
277
+ },
278
+ });
176
279
  try {
177
280
  const { streamText } = await import('ai');
178
- let lastError = null;
179
281
  const onError = ({ error }) => {
180
282
  if (parsed.provider === 'anthropic') {
181
283
  lastError = normalizeAnthropicModelAccessError(error, parsed.model) ?? error;
@@ -199,7 +301,7 @@ export async function streamTextWithModelId({ modelId, apiKeys, system, prompt,
199
301
  onError,
200
302
  });
201
303
  return {
202
- textStream: result.textStream,
304
+ textStream: wrapTextStream(result.textStream),
203
305
  canonicalModelId: parsed.canonical,
204
306
  provider: parsed.provider,
205
307
  usage: result.totalUsage.then((raw) => normalizeTokenUsage(raw)).catch(() => null),
@@ -222,7 +324,7 @@ export async function streamTextWithModelId({ modelId, apiKeys, system, prompt,
222
324
  onError,
223
325
  });
224
326
  return {
225
- textStream: result.textStream,
327
+ textStream: wrapTextStream(result.textStream),
226
328
  canonicalModelId: parsed.canonical,
227
329
  provider: parsed.provider,
228
330
  usage: result.totalUsage.then((raw) => normalizeTokenUsage(raw)).catch(() => null),
@@ -245,20 +347,22 @@ export async function streamTextWithModelId({ modelId, apiKeys, system, prompt,
245
347
  onError,
246
348
  });
247
349
  return {
248
- textStream: result.textStream,
350
+ textStream: wrapTextStream(result.textStream),
249
351
  canonicalModelId: parsed.canonical,
250
352
  provider: parsed.provider,
251
353
  usage: result.totalUsage.then((raw) => normalizeTokenUsage(raw)).catch(() => null),
252
354
  lastError: () => lastError,
253
355
  };
254
356
  }
255
- const apiKey = apiKeys.openaiApiKey;
256
- if (!apiKey)
257
- throw new Error('Missing OPENAI_API_KEY for openai/... model');
258
357
  const { createOpenAI } = await import('@ai-sdk/openai');
259
- const openai = createOpenAI({ apiKey, fetch: fetchImpl });
260
- const baseUrl = typeof process !== 'undefined' ? process.env.OPENAI_BASE_URL : undefined;
261
- const useChatCompletions = typeof baseUrl === 'string' && /openrouter\.ai/i.test(baseUrl) && baseUrl.length > 0;
358
+ const openaiConfig = resolveOpenAiClientConfig({ apiKeys, openrouter, fetchImpl });
359
+ const openai = createOpenAI({
360
+ apiKey: openaiConfig.apiKey,
361
+ ...(openaiConfig.baseURL ? { baseURL: openaiConfig.baseURL } : {}),
362
+ fetch: openaiConfig.fetch,
363
+ });
364
+ // OpenRouter requires chat completions endpoint
365
+ const useChatCompletions = openaiConfig.useChatCompletions;
262
366
  const responsesModelId = parsed.model;
263
367
  const chatModelId = parsed.model;
264
368
  const result = streamText({
@@ -271,7 +375,7 @@ export async function streamTextWithModelId({ modelId, apiKeys, system, prompt,
271
375
  onError,
272
376
  });
273
377
  return {
274
- textStream: result.textStream,
378
+ textStream: wrapTextStream(result.textStream),
275
379
  canonicalModelId: parsed.canonical,
276
380
  provider: parsed.provider,
277
381
  usage: result.totalUsage.then((raw) => normalizeTokenUsage(raw)).catch(() => null),
@@ -290,7 +394,7 @@ export async function streamTextWithModelId({ modelId, apiKeys, system, prompt,
290
394
  throw error;
291
395
  }
292
396
  finally {
293
- clearTimeout(timeout);
397
+ stopTimeout();
294
398
  }
295
399
  }
296
400
  //# sourceMappingURL=generate-text.js.map