@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.
- package/CHANGELOG.md +66 -3
- package/README.md +40 -6
- package/dist/cli.cjs +6502 -634
- package/dist/cli.cjs.map +4 -4
- package/dist/esm/content/asset.js +18 -0
- package/dist/esm/content/asset.js.map +1 -1
- package/dist/esm/content/link-preview/client.js +8 -0
- package/dist/esm/content/link-preview/client.js.map +1 -1
- package/dist/esm/content/link-preview/content/article.js +15 -1
- package/dist/esm/content/link-preview/content/article.js.map +1 -1
- package/dist/esm/content/link-preview/content/index.js +151 -4
- package/dist/esm/content/link-preview/content/index.js.map +1 -1
- package/dist/esm/content/link-preview/transcript/index.js +6 -0
- package/dist/esm/content/link-preview/transcript/index.js.map +1 -1
- package/dist/esm/content/link-preview/transcript/providers/youtube/yt-dlp.js +213 -0
- package/dist/esm/content/link-preview/transcript/providers/youtube/yt-dlp.js.map +1 -0
- package/dist/esm/content/link-preview/transcript/providers/youtube.js +40 -2
- package/dist/esm/content/link-preview/transcript/providers/youtube.js.map +1 -1
- package/dist/esm/flags.js +14 -2
- package/dist/esm/flags.js.map +1 -1
- package/dist/esm/llm/generate-text.js +125 -21
- package/dist/esm/llm/generate-text.js.map +1 -1
- package/dist/esm/llm/html-to-markdown.js +3 -2
- package/dist/esm/llm/html-to-markdown.js.map +1 -1
- package/dist/esm/pricing/litellm.js +4 -1
- package/dist/esm/pricing/litellm.js.map +1 -1
- package/dist/esm/prompts/file.js +15 -4
- package/dist/esm/prompts/file.js.map +1 -1
- package/dist/esm/prompts/link-summary.js +20 -6
- package/dist/esm/prompts/link-summary.js.map +1 -1
- package/dist/esm/run.js +545 -407
- package/dist/esm/run.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/types/content/link-preview/client.d.ts +5 -1
- package/dist/types/content/link-preview/content/types.d.ts +1 -1
- package/dist/types/content/link-preview/deps.d.ts +33 -0
- package/dist/types/content/link-preview/transcript/providers/youtube/yt-dlp.d.ts +15 -0
- package/dist/types/content/link-preview/transcript/types.d.ts +4 -0
- package/dist/types/content/link-preview/types.d.ts +1 -1
- package/dist/types/costs.d.ts +1 -1
- package/dist/types/flags.d.ts +1 -1
- package/dist/types/llm/generate-text.d.ts +8 -2
- package/dist/types/llm/html-to-markdown.d.ts +4 -1
- package/dist/types/pricing/litellm.d.ts +1 -0
- package/dist/types/prompts/file.d.ts +2 -1
- package/dist/types/version.d.ts +1 -1
- package/docs/extract-only.md +1 -1
- package/docs/firecrawl.md +2 -2
- package/docs/llm.md +7 -0
- package/docs/site/docs/config.html +1 -1
- package/docs/site/docs/firecrawl.html +1 -1
- package/docs/website.md +3 -3
- package/docs/youtube.md +5 -2
- 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
|
|
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
|
|
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":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAA;AAOzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EACL,+BAA+B,EAC/B,qCAAqC,GACtC,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAA;AAC7D,OAAO,EAAE,gCAAgC,EAAE,MAAM,uBAAuB,CAAA;
|
|
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
|
-
|
|
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
|
-
|
|
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
|
package/dist/esm/flags.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flags.js","sourceRoot":"","sources":["../../src/flags.ts"],"names":[],"mappings":"AAaA,MAAM,eAAe,GAAoB,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;AACjF,MAAM,gBAAgB,GAAG,8CAA8C,CAAA;AACvE,MAAM,aAAa,GAAG,yCAAyC,CAAA;
|
|
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
|
-
|
|
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
|
|
138
|
-
const
|
|
139
|
-
|
|
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
|
-
|
|
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
|
|
260
|
-
const
|
|
261
|
-
|
|
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
|
-
|
|
397
|
+
stopTimeout();
|
|
294
398
|
}
|
|
295
399
|
}
|
|
296
400
|
//# sourceMappingURL=generate-text.js.map
|