@steipete/summarize 0.10.0 → 0.11.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/CHANGELOG.md +80 -28
- package/README.md +115 -30
- package/dist/cli.js +1 -1
- package/dist/esm/cache.js +67 -65
- package/dist/esm/cache.js.map +1 -1
- package/dist/esm/cli-main.js +27 -27
- package/dist/esm/cli-main.js.map +1 -1
- package/dist/esm/cli.js +2 -2
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/config.js +310 -166
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/content/asset.js +53 -50
- package/dist/esm/content/asset.js.map +1 -1
- package/dist/esm/content/index.js +1 -1
- package/dist/esm/content/index.js.map +1 -1
- package/dist/esm/costs.js +1 -1
- package/dist/esm/costs.js.map +1 -1
- package/dist/esm/daemon/agent.js +165 -164
- package/dist/esm/daemon/agent.js.map +1 -1
- package/dist/esm/daemon/auto-mode.js +3 -3
- package/dist/esm/daemon/auto-mode.js.map +1 -1
- package/dist/esm/daemon/chat.js +16 -14
- package/dist/esm/daemon/chat.js.map +1 -1
- package/dist/esm/daemon/cli-entrypoint.js +72 -0
- package/dist/esm/daemon/cli-entrypoint.js.map +1 -0
- package/dist/esm/daemon/cli.js +63 -87
- package/dist/esm/daemon/cli.js.map +1 -1
- package/dist/esm/daemon/config.js +15 -15
- package/dist/esm/daemon/config.js.map +1 -1
- package/dist/esm/daemon/constants.js +6 -6
- package/dist/esm/daemon/constants.js.map +1 -1
- package/dist/esm/daemon/env-merge.js.map +1 -1
- package/dist/esm/daemon/env-snapshot.js +36 -31
- package/dist/esm/daemon/env-snapshot.js.map +1 -1
- package/dist/esm/daemon/flow-context.js +59 -28
- package/dist/esm/daemon/flow-context.js.map +1 -1
- package/dist/esm/daemon/launchd.js +100 -55
- package/dist/esm/daemon/launchd.js.map +1 -1
- package/dist/esm/daemon/meta.js +5 -5
- package/dist/esm/daemon/meta.js.map +1 -1
- package/dist/esm/daemon/models.js +54 -31
- package/dist/esm/daemon/models.js.map +1 -1
- package/dist/esm/daemon/process-registry.js +15 -15
- package/dist/esm/daemon/process-registry.js.map +1 -1
- package/dist/esm/daemon/schtasks.js +42 -42
- package/dist/esm/daemon/schtasks.js.map +1 -1
- package/dist/esm/daemon/server.js +248 -244
- package/dist/esm/daemon/server.js.map +1 -1
- package/dist/esm/daemon/summarize-progress.js +11 -11
- package/dist/esm/daemon/summarize-progress.js.map +1 -1
- package/dist/esm/daemon/summarize.js +29 -29
- package/dist/esm/daemon/summarize.js.map +1 -1
- package/dist/esm/daemon/systemd.js +47 -47
- package/dist/esm/daemon/systemd.js.map +1 -1
- package/dist/esm/firecrawl.js +12 -12
- package/dist/esm/firecrawl.js.map +1 -1
- package/dist/esm/flags.js +32 -32
- package/dist/esm/flags.js.map +1 -1
- package/dist/esm/index.js +3 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/language.js +1 -1
- package/dist/esm/language.js.map +1 -1
- package/dist/esm/llm/cli.js +128 -64
- package/dist/esm/llm/cli.js.map +1 -1
- package/dist/esm/llm/errors.js +1 -1
- package/dist/esm/llm/errors.js.map +1 -1
- package/dist/esm/llm/generate-text.js +107 -98
- package/dist/esm/llm/generate-text.js.map +1 -1
- package/dist/esm/llm/google-models.js +17 -17
- package/dist/esm/llm/google-models.js.map +1 -1
- package/dist/esm/llm/html-to-markdown.js +3 -3
- package/dist/esm/llm/html-to-markdown.js.map +1 -1
- package/dist/esm/llm/model-id.js +38 -16
- package/dist/esm/llm/model-id.js.map +1 -1
- package/dist/esm/llm/prompt.js +5 -5
- package/dist/esm/llm/prompt.js.map +1 -1
- package/dist/esm/llm/providers/anthropic.js +33 -33
- package/dist/esm/llm/providers/anthropic.js.map +1 -1
- package/dist/esm/llm/providers/google.js +19 -19
- package/dist/esm/llm/providers/google.js.map +1 -1
- package/dist/esm/llm/providers/models.js +30 -30
- package/dist/esm/llm/providers/models.js.map +1 -1
- package/dist/esm/llm/providers/openai.js +35 -34
- package/dist/esm/llm/providers/openai.js.map +1 -1
- package/dist/esm/llm/providers/shared.js +8 -8
- package/dist/esm/llm/providers/shared.js.map +1 -1
- package/dist/esm/llm/transcript-to-markdown.js +9 -5
- package/dist/esm/llm/transcript-to-markdown.js.map +1 -1
- package/dist/esm/llm/usage.js +18 -18
- package/dist/esm/llm/usage.js.map +1 -1
- package/dist/esm/logging/daemon.js +21 -21
- package/dist/esm/logging/daemon.js.map +1 -1
- package/dist/esm/logging/ring-file.js +5 -5
- package/dist/esm/logging/ring-file.js.map +1 -1
- package/dist/esm/markitdown.js +21 -19
- package/dist/esm/markitdown.js.map +1 -1
- package/dist/esm/media-cache.js +39 -39
- package/dist/esm/media-cache.js.map +1 -1
- package/dist/esm/model-auto.js +175 -106
- package/dist/esm/model-auto.js.map +1 -1
- package/dist/esm/model-spec.js +52 -42
- package/dist/esm/model-spec.js.map +1 -1
- package/dist/esm/pricing/litellm.js +4 -4
- package/dist/esm/pricing/litellm.js.map +1 -1
- package/dist/esm/processes.js +1 -1
- package/dist/esm/processes.js.map +1 -1
- package/dist/esm/prompts/index.js +1 -1
- package/dist/esm/prompts/index.js.map +1 -1
- package/dist/esm/refresh-free.js +81 -81
- package/dist/esm/refresh-free.js.map +1 -1
- package/dist/esm/run/attachments.js +47 -44
- package/dist/esm/run/attachments.js.map +1 -1
- package/dist/esm/run/bird.js +26 -26
- package/dist/esm/run/bird.js.map +1 -1
- package/dist/esm/run/cache-state.js +7 -7
- package/dist/esm/run/cache-state.js.map +1 -1
- package/dist/esm/run/cli-fallback-state.js +45 -0
- package/dist/esm/run/cli-fallback-state.js.map +1 -0
- package/dist/esm/run/cli-preflight.js +24 -24
- package/dist/esm/run/cli-preflight.js.map +1 -1
- package/dist/esm/run/constants.js +12 -12
- package/dist/esm/run/constants.js.map +1 -1
- package/dist/esm/run/cookies/twitter.js +47 -47
- package/dist/esm/run/cookies/twitter.js.map +1 -1
- package/dist/esm/run/env.js +21 -15
- package/dist/esm/run/env.js.map +1 -1
- package/dist/esm/run/fetch-with-timeout.js +4 -4
- package/dist/esm/run/fetch-with-timeout.js.map +1 -1
- package/dist/esm/run/finish-line.js +68 -68
- package/dist/esm/run/finish-line.js.map +1 -1
- package/dist/esm/run/flows/asset/extract.js +15 -15
- package/dist/esm/run/flows/asset/extract.js.map +1 -1
- package/dist/esm/run/flows/asset/input.js +47 -66
- package/dist/esm/run/flows/asset/input.js.map +1 -1
- package/dist/esm/run/flows/asset/media-policy.js +1 -1
- package/dist/esm/run/flows/asset/media-policy.js.map +1 -1
- package/dist/esm/run/flows/asset/media.js +49 -40
- package/dist/esm/run/flows/asset/media.js.map +1 -1
- package/dist/esm/run/flows/asset/output.js +12 -12
- package/dist/esm/run/flows/asset/output.js.map +1 -1
- package/dist/esm/run/flows/asset/preprocess.js +79 -44
- package/dist/esm/run/flows/asset/preprocess.js.map +1 -1
- package/dist/esm/run/flows/asset/summary.js +173 -106
- package/dist/esm/run/flows/asset/summary.js.map +1 -1
- package/dist/esm/run/flows/url/extract.js +26 -26
- package/dist/esm/run/flows/url/extract.js.map +1 -1
- package/dist/esm/run/flows/url/flow.js +104 -98
- package/dist/esm/run/flows/url/flow.js.map +1 -1
- package/dist/esm/run/flows/url/markdown.js +57 -57
- package/dist/esm/run/flows/url/markdown.js.map +1 -1
- package/dist/esm/run/flows/url/slides-output.js +61 -59
- package/dist/esm/run/flows/url/slides-output.js.map +1 -1
- package/dist/esm/run/flows/url/slides-text.js +85 -85
- package/dist/esm/run/flows/url/slides-text.js.map +1 -1
- package/dist/esm/run/flows/url/summary.js +174 -107
- package/dist/esm/run/flows/url/summary.js.map +1 -1
- package/dist/esm/run/format.js +10 -10
- package/dist/esm/run/format.js.map +1 -1
- package/dist/esm/run/help.js +141 -135
- package/dist/esm/run/help.js.map +1 -1
- package/dist/esm/run/logging.js +10 -10
- package/dist/esm/run/logging.js.map +1 -1
- package/dist/esm/run/markdown.js +12 -12
- package/dist/esm/run/markdown.js.map +1 -1
- package/dist/esm/run/media-cache-state.js +5 -5
- package/dist/esm/run/media-cache-state.js.map +1 -1
- package/dist/esm/run/model-attempts.js.map +1 -1
- package/dist/esm/run/openrouter.js +11 -11
- package/dist/esm/run/openrouter.js.map +1 -1
- package/dist/esm/run/progress.js +1 -1
- package/dist/esm/run/progress.js.map +1 -1
- package/dist/esm/run/run-config.js +16 -16
- package/dist/esm/run/run-config.js.map +1 -1
- package/dist/esm/run/run-context.js +2 -2
- package/dist/esm/run/run-context.js.map +1 -1
- package/dist/esm/run/run-env.js +55 -54
- package/dist/esm/run/run-env.js.map +1 -1
- package/dist/esm/run/run-input.js +3 -3
- package/dist/esm/run/run-input.js.map +1 -1
- package/dist/esm/run/run-metrics.js +16 -16
- package/dist/esm/run/run-metrics.js.map +1 -1
- package/dist/esm/run/run-models.js +28 -23
- package/dist/esm/run/run-models.js.map +1 -1
- package/dist/esm/run/run-output.js +3 -3
- package/dist/esm/run/run-output.js.map +1 -1
- package/dist/esm/run/run-settings.js +83 -34
- package/dist/esm/run/run-settings.js.map +1 -1
- package/dist/esm/run/run-stream.js +4 -4
- package/dist/esm/run/run-stream.js.map +1 -1
- package/dist/esm/run/runner.js +166 -127
- package/dist/esm/run/runner.js.map +1 -1
- package/dist/esm/run/slides-cli.js +43 -42
- package/dist/esm/run/slides-cli.js.map +1 -1
- package/dist/esm/run/slides-render.js +36 -36
- package/dist/esm/run/slides-render.js.map +1 -1
- package/dist/esm/run/stdin-temp-file.js +77 -0
- package/dist/esm/run/stdin-temp-file.js.map +1 -0
- package/dist/esm/run/stream-output.js +7 -7
- package/dist/esm/run/stream-output.js.map +1 -1
- package/dist/esm/run/streaming.js +16 -16
- package/dist/esm/run/streaming.js.map +1 -1
- package/dist/esm/run/summary-engine.js +57 -51
- package/dist/esm/run/summary-engine.js.map +1 -1
- package/dist/esm/run/summary-llm.js +3 -3
- package/dist/esm/run/summary-llm.js.map +1 -1
- package/dist/esm/run/terminal.js +4 -4
- package/dist/esm/run/terminal.js.map +1 -1
- package/dist/esm/run/tips.js +2 -2
- package/dist/esm/run/tips.js.map +1 -1
- package/dist/esm/run/transcriber-cli.js +49 -49
- package/dist/esm/run/transcriber-cli.js.map +1 -1
- package/dist/esm/run.js +1 -1
- package/dist/esm/run.js.map +1 -1
- package/dist/esm/shared/contracts.js +1 -1
- package/dist/esm/shared/contracts.js.map +1 -1
- package/dist/esm/shared/sse-events.js +16 -16
- package/dist/esm/shared/sse-events.js.map +1 -1
- package/dist/esm/shared/streaming-merge.js +3 -3
- package/dist/esm/shared/streaming-merge.js.map +1 -1
- package/dist/esm/slides/extract.js +258 -249
- package/dist/esm/slides/extract.js.map +1 -1
- package/dist/esm/slides/index.js +3 -3
- package/dist/esm/slides/index.js.map +1 -1
- package/dist/esm/slides/settings.js +14 -14
- package/dist/esm/slides/settings.js.map +1 -1
- package/dist/esm/slides/store.js +9 -9
- package/dist/esm/slides/store.js.map +1 -1
- package/dist/esm/tty/format.js +13 -13
- package/dist/esm/tty/format.js.map +1 -1
- package/dist/esm/tty/osc-progress.js +1 -1
- package/dist/esm/tty/osc-progress.js.map +1 -1
- package/dist/esm/tty/progress/fetch-html.js +14 -14
- package/dist/esm/tty/progress/fetch-html.js.map +1 -1
- package/dist/esm/tty/progress/transcript.js +70 -62
- package/dist/esm/tty/progress/transcript.js.map +1 -1
- package/dist/esm/tty/spinner.js +20 -9
- package/dist/esm/tty/spinner.js.map +1 -1
- package/dist/esm/tty/theme.js +92 -92
- package/dist/esm/tty/theme.js.map +1 -1
- package/dist/esm/tty/website-progress.js +32 -32
- package/dist/esm/tty/website-progress.js.map +1 -1
- package/dist/esm/version.js +29 -29
- package/dist/esm/version.js.map +1 -1
- package/dist/types/cache.d.ts +6 -6
- package/dist/types/config.d.ts +49 -7
- package/dist/types/content/asset.d.ts +8 -6
- package/dist/types/content/index.d.ts +1 -1
- package/dist/types/costs.d.ts +3 -3
- package/dist/types/daemon/agent.d.ts +1 -1
- package/dist/types/daemon/auto-mode.d.ts +3 -3
- package/dist/types/daemon/chat.d.ts +2 -2
- package/dist/types/daemon/cli-entrypoint.d.ts +2 -0
- package/dist/types/daemon/config.d.ts +2 -2
- package/dist/types/daemon/env-merge.d.ts +1 -1
- package/dist/types/daemon/env-snapshot.d.ts +1 -1
- package/dist/types/daemon/flow-context.d.ts +7 -7
- package/dist/types/daemon/launchd.d.ts +8 -0
- package/dist/types/daemon/models.d.ts +6 -2
- package/dist/types/daemon/process-registry.d.ts +5 -5
- package/dist/types/daemon/server.d.ts +2 -2
- package/dist/types/daemon/summarize-progress.d.ts +1 -1
- package/dist/types/daemon/summarize.d.ts +7 -7
- package/dist/types/firecrawl.d.ts +1 -1
- package/dist/types/flags.d.ts +11 -11
- package/dist/types/index.d.ts +4 -4
- package/dist/types/language.d.ts +1 -1
- package/dist/types/llm/attachments.d.ts +1 -1
- package/dist/types/llm/cli.d.ts +3 -3
- package/dist/types/llm/generate-text.d.ts +7 -7
- package/dist/types/llm/html-to-markdown.d.ts +3 -3
- package/dist/types/llm/model-id.d.ts +1 -1
- package/dist/types/llm/prompt.d.ts +2 -2
- package/dist/types/llm/providers/anthropic.d.ts +3 -3
- package/dist/types/llm/providers/google.d.ts +3 -3
- package/dist/types/llm/providers/models.d.ts +2 -2
- package/dist/types/llm/providers/openai.d.ts +4 -4
- package/dist/types/llm/providers/shared.d.ts +2 -2
- package/dist/types/llm/transcript-to-markdown.d.ts +4 -2
- package/dist/types/llm/usage.d.ts +1 -1
- package/dist/types/logging/daemon.d.ts +4 -4
- package/dist/types/markitdown.d.ts +1 -1
- package/dist/types/media-cache.d.ts +2 -2
- package/dist/types/model-auto.d.ts +14 -4
- package/dist/types/model-spec.d.ts +10 -10
- package/dist/types/pricing/litellm.d.ts +1 -1
- package/dist/types/processes.d.ts +1 -1
- package/dist/types/prompts/index.d.ts +1 -1
- package/dist/types/run/attachments.d.ts +7 -7
- package/dist/types/run/bird.d.ts +2 -2
- package/dist/types/run/cache-state.d.ts +2 -2
- package/dist/types/run/cli-fallback-state.d.ts +6 -0
- package/dist/types/run/constants.d.ts +1 -1
- package/dist/types/run/cookies/twitter.d.ts +1 -1
- package/dist/types/run/env.d.ts +1 -1
- package/dist/types/run/finish-line.d.ts +5 -5
- package/dist/types/run/flows/asset/extract.d.ts +4 -4
- package/dist/types/run/flows/asset/input.d.ts +9 -3
- package/dist/types/run/flows/asset/media.d.ts +1 -1
- package/dist/types/run/flows/asset/output.d.ts +5 -5
- package/dist/types/run/flows/asset/preprocess.d.ts +23 -17
- package/dist/types/run/flows/asset/summary.d.ts +19 -17
- package/dist/types/run/flows/url/extract.d.ts +1 -1
- package/dist/types/run/flows/url/flow.d.ts +1 -1
- package/dist/types/run/flows/url/markdown.d.ts +6 -6
- package/dist/types/run/flows/url/slides-output.d.ts +7 -7
- package/dist/types/run/flows/url/slides-text.d.ts +9 -9
- package/dist/types/run/flows/url/summary.d.ts +11 -11
- package/dist/types/run/flows/url/types.d.ts +26 -22
- package/dist/types/run/format.d.ts +3 -3
- package/dist/types/run/help.d.ts +1 -1
- package/dist/types/run/media-cache-state.d.ts +2 -2
- package/dist/types/run/model-attempts.d.ts +1 -1
- package/dist/types/run/run-config.d.ts +4 -4
- package/dist/types/run/run-context.d.ts +3 -1
- package/dist/types/run/run-env.d.ts +3 -1
- package/dist/types/run/run-input.d.ts +2 -2
- package/dist/types/run/run-metrics.d.ts +3 -3
- package/dist/types/run/run-models.d.ts +3 -2
- package/dist/types/run/run-output.d.ts +1 -1
- package/dist/types/run/run-settings.d.ts +15 -6
- package/dist/types/run/run-stream.d.ts +2 -2
- package/dist/types/run/runner.d.ts +3 -2
- package/dist/types/run/slides-render.d.ts +4 -4
- package/dist/types/run/stdin-temp-file.d.ts +9 -0
- package/dist/types/run/stream-output.d.ts +1 -1
- package/dist/types/run/streaming.d.ts +4 -4
- package/dist/types/run/summary-engine.d.ts +11 -11
- package/dist/types/run/summary-llm.d.ts +5 -5
- package/dist/types/run/types.d.ts +4 -4
- package/dist/types/run.d.ts +1 -1
- package/dist/types/shared/contracts.d.ts +2 -2
- package/dist/types/shared/sse-events.d.ts +9 -9
- package/dist/types/slides/extract.d.ts +5 -4
- package/dist/types/slides/index.d.ts +5 -5
- package/dist/types/slides/store.d.ts +2 -2
- package/dist/types/slides/types.d.ts +2 -2
- package/dist/types/tty/osc-progress.d.ts +5 -5
- package/dist/types/tty/progress/fetch-html.d.ts +3 -3
- package/dist/types/tty/progress/transcript.d.ts +3 -3
- package/dist/types/tty/spinner.d.ts +2 -2
- package/dist/types/tty/theme.d.ts +2 -2
- package/dist/types/tty/website-progress.d.ts +3 -3
- package/dist/types/version.d.ts +1 -1
- package/docs/agent.md +38 -4
- package/docs/assets/site.js +46 -46
- package/docs/chrome-extension.md +11 -5
- package/docs/cli.md +59 -13
- package/docs/config.md +59 -10
- package/docs/extract-only.md +2 -0
- package/docs/index.html +33 -14
- package/docs/llm.md +7 -4
- package/docs/media.md +5 -4
- package/docs/model-auto.md +3 -2
- package/docs/nvidia-onnx-transcription.md +3 -3
- package/docs/openai.md +1 -1
- package/docs/releasing.md +3 -0
- package/docs/site/404.html +4 -1
- package/docs/site/assets/site.js +46 -46
- package/docs/site/docs/chrome-extension.html +18 -6
- package/docs/site/docs/config.html +29 -8
- package/docs/site/docs/extract-only.html +16 -4
- package/docs/site/docs/firecrawl.html +12 -3
- package/docs/site/docs/index.html +35 -6
- package/docs/site/docs/llm.html +19 -5
- package/docs/site/docs/openai.html +18 -5
- package/docs/site/docs/website.html +29 -9
- package/docs/site/docs/youtube.html +12 -3
- package/docs/site/index.html +33 -14
- package/docs/slides.md +13 -5
- package/docs/smoketest.md +29 -20
- package/docs/timestamps.md +21 -0
- package/docs/website.md +2 -1
- package/docs/youtube.md +4 -0
- package/package.json +36 -35
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import { isTwitterStatusUrl, isYouTubeUrl } from
|
|
2
|
-
import { countTokens } from
|
|
3
|
-
import { render as renderMarkdownAnsi } from
|
|
4
|
-
import { buildLanguageKey, buildLengthKey, buildPromptHash, buildSummaryCacheKey, hashString, normalizeContentForHash, } from
|
|
5
|
-
import { formatOutputLanguageForJson } from
|
|
6
|
-
import { parseGatewayStyleModelId } from
|
|
7
|
-
import { buildAutoModelAttempts } from
|
|
8
|
-
import { buildLinkSummaryPrompt, SUMMARY_LENGTH_TARGET_CHARACTERS, SUMMARY_SYSTEM_PROMPT, } from
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
1
|
+
import { isTwitterStatusUrl, isYouTubeUrl } from "@steipete/summarize-core/content/url";
|
|
2
|
+
import { countTokens } from "gpt-tokenizer";
|
|
3
|
+
import { render as renderMarkdownAnsi } from "markdansi";
|
|
4
|
+
import { buildLanguageKey, buildLengthKey, buildPromptHash, buildSummaryCacheKey, hashString, normalizeContentForHash, } from "../../../cache.js";
|
|
5
|
+
import { formatOutputLanguageForJson } from "../../../language.js";
|
|
6
|
+
import { parseGatewayStyleModelId } from "../../../llm/model-id.js";
|
|
7
|
+
import { buildAutoModelAttempts } from "../../../model-auto.js";
|
|
8
|
+
import { buildLinkSummaryPrompt, SUMMARY_LENGTH_TARGET_CHARACTERS, SUMMARY_SYSTEM_PROMPT, } from "../../../prompts/index.js";
|
|
9
|
+
import { readLastSuccessfulCliProvider, writeLastSuccessfulCliProvider, } from "../../cli-fallback-state.js";
|
|
10
|
+
import { parseCliUserModelId } from "../../env.js";
|
|
11
|
+
import { buildExtractFinishLabel, buildLengthPartsForFinishLine, writeFinishLine, } from "../../finish-line.js";
|
|
12
|
+
import { resolveTargetCharacters } from "../../format.js";
|
|
13
|
+
import { writeVerbose } from "../../logging.js";
|
|
14
|
+
import { prepareMarkdownForTerminal } from "../../markdown.js";
|
|
15
|
+
import { runModelAttempts } from "../../model-attempts.js";
|
|
16
|
+
import { buildOpenRouterNoAllowedProvidersMessage } from "../../openrouter.js";
|
|
17
|
+
import { isRichTty, markdownRenderWidth, supportsColor } from "../../terminal.js";
|
|
18
|
+
import { coerceSummaryWithSlides, interleaveSlidesIntoTranscript, normalizeSummarySlideHeadings, } from "./slides-text.js";
|
|
18
19
|
const MAX_SLIDE_TRANSCRIPT_CHARS_BY_PRESET = {
|
|
19
20
|
short: 2500,
|
|
20
21
|
medium: 5000,
|
|
@@ -25,7 +26,7 @@ const MAX_SLIDE_TRANSCRIPT_CHARS_BY_PRESET = {
|
|
|
25
26
|
const SLIDE_TRANSCRIPT_DEFAULT_EDGE_SECONDS = 30;
|
|
26
27
|
const SLIDE_TRANSCRIPT_LEEWAY_SECONDS = 10;
|
|
27
28
|
function parseTimestampSeconds(value) {
|
|
28
|
-
const parts = value.split(
|
|
29
|
+
const parts = value.split(":").map((item) => Number(item));
|
|
29
30
|
if (parts.some((item) => !Number.isFinite(item)))
|
|
30
31
|
return null;
|
|
31
32
|
if (parts.length === 2) {
|
|
@@ -42,9 +43,9 @@ function parseTranscriptTimedText(input) {
|
|
|
42
43
|
if (!input)
|
|
43
44
|
return [];
|
|
44
45
|
const segments = [];
|
|
45
|
-
for (const line of input.split(
|
|
46
|
+
for (const line of input.split("\n")) {
|
|
46
47
|
const trimmed = line.trim();
|
|
47
|
-
if (!trimmed.startsWith(
|
|
48
|
+
if (!trimmed.startsWith("["))
|
|
48
49
|
continue;
|
|
49
50
|
const match = trimmed.match(/^\[(\d{1,2}:\d{2}(?::\d{2})?)\]\s*(.*)$/);
|
|
50
51
|
if (!match)
|
|
@@ -52,7 +53,7 @@ function parseTranscriptTimedText(input) {
|
|
|
52
53
|
const seconds = parseTimestampSeconds(match[1]);
|
|
53
54
|
if (seconds == null)
|
|
54
55
|
continue;
|
|
55
|
-
const text = (match[2] ??
|
|
56
|
+
const text = (match[2] ?? "").trim();
|
|
56
57
|
if (!text)
|
|
57
58
|
continue;
|
|
58
59
|
segments.push({ startSeconds: seconds, text });
|
|
@@ -65,20 +66,20 @@ function formatTimestamp(seconds) {
|
|
|
65
66
|
const hours = Math.floor(clamped / 3600);
|
|
66
67
|
const minutes = Math.floor((clamped % 3600) / 60);
|
|
67
68
|
const secs = clamped % 60;
|
|
68
|
-
const mm = String(minutes).padStart(2,
|
|
69
|
-
const ss = String(secs).padStart(2,
|
|
69
|
+
const mm = String(minutes).padStart(2, "0");
|
|
70
|
+
const ss = String(secs).padStart(2, "0");
|
|
70
71
|
if (hours <= 0)
|
|
71
72
|
return `${minutes}:${ss}`;
|
|
72
|
-
const hh = String(hours).padStart(2,
|
|
73
|
+
const hh = String(hours).padStart(2, "0");
|
|
73
74
|
return `${hh}:${mm}:${ss}`;
|
|
74
75
|
}
|
|
75
76
|
function truncateTranscript(value, limit) {
|
|
76
77
|
if (value.length <= limit)
|
|
77
78
|
return value;
|
|
78
79
|
const truncated = value.slice(0, limit).trimEnd();
|
|
79
|
-
const clean = truncated.replace(/\s+\S*$/,
|
|
80
|
+
const clean = truncated.replace(/\s+\S*$/, "").trim();
|
|
80
81
|
const result = clean.length > 0 ? clean : truncated.trim();
|
|
81
|
-
return result.length > 0 ? `${result}…` :
|
|
82
|
+
return result.length > 0 ? `${result}…` : "";
|
|
82
83
|
}
|
|
83
84
|
function buildSlidesPromptText({ slides, transcriptTimedText, preset, }) {
|
|
84
85
|
if (!slides || slides.slides.length === 0)
|
|
@@ -114,19 +115,19 @@ function buildSlidesPromptText({ slides, transcriptTimedText, preset, }) {
|
|
|
114
115
|
break;
|
|
115
116
|
excerptParts.push(segment.text);
|
|
116
117
|
}
|
|
117
|
-
const excerptRaw = excerptParts.join(
|
|
118
|
+
const excerptRaw = excerptParts.join(" ").trim().replace(/\s+/g, " ");
|
|
118
119
|
const excerptBudget = remaining > 0 ? Math.min(perSlideBudget, remaining) : 0;
|
|
119
|
-
const excerpt = excerptRaw && excerptBudget > 0 ? truncateTranscript(excerptRaw, excerptBudget) :
|
|
120
|
+
const excerpt = excerptRaw && excerptBudget > 0 ? truncateTranscript(excerptRaw, excerptBudget) : "";
|
|
120
121
|
const label = `[slide:${slide.index}] [${formatTimestamp(start)}–${formatTimestamp(end)}]`;
|
|
121
122
|
const block = excerpt ? `${label}\n${excerpt}` : label;
|
|
122
123
|
blocks.push(block);
|
|
123
124
|
remaining = Math.max(0, remaining - block.length);
|
|
124
125
|
}
|
|
125
|
-
return blocks.length > 0 ? blocks.join(
|
|
126
|
+
return blocks.length > 0 ? blocks.join("\n\n") : null;
|
|
126
127
|
}
|
|
127
128
|
export function buildUrlPrompt({ extracted, outputLanguage, lengthArg, promptOverride, lengthInstruction, languageInstruction, slides, }) {
|
|
128
|
-
const isYouTube = extracted.siteName ===
|
|
129
|
-
const preset = lengthArg.kind ===
|
|
129
|
+
const isYouTube = extracted.siteName === "YouTube";
|
|
130
|
+
const preset = lengthArg.kind === "preset" ? lengthArg.preset : "medium";
|
|
130
131
|
const slidesText = buildSlidesPromptText({
|
|
131
132
|
slides,
|
|
132
133
|
transcriptTimedText: extracted.transcriptTimedText,
|
|
@@ -140,10 +141,10 @@ export function buildUrlPrompt({ extracted, outputLanguage, lengthArg, promptOve
|
|
|
140
141
|
content: extracted.content,
|
|
141
142
|
truncated: extracted.truncated,
|
|
142
143
|
hasTranscript: isYouTube ||
|
|
143
|
-
(extracted.transcriptSource !== null && extracted.transcriptSource !==
|
|
144
|
+
(extracted.transcriptSource !== null && extracted.transcriptSource !== "unavailable"),
|
|
144
145
|
hasTranscriptTimestamps: Boolean(extracted.transcriptTimedText),
|
|
145
146
|
slides: slidesText ? { count: slides?.slides.length ?? 0, text: slidesText } : null,
|
|
146
|
-
summaryLength: lengthArg.kind ===
|
|
147
|
+
summaryLength: lengthArg.kind === "preset" ? lengthArg.preset : { maxCharacters: lengthArg.maxCharacters },
|
|
147
148
|
outputLanguage,
|
|
148
149
|
shares: [],
|
|
149
150
|
promptOverride: promptOverride ?? null,
|
|
@@ -161,7 +162,7 @@ function shouldBypassShortContentSummary({ extracted, lengthArg, forceSummary, m
|
|
|
161
162
|
return false;
|
|
162
163
|
if (extracted.content.length > targetCharacters)
|
|
163
164
|
return false;
|
|
164
|
-
if (!json && typeof maxOutputTokensArg ===
|
|
165
|
+
if (!json && typeof maxOutputTokensArg === "number") {
|
|
165
166
|
const tokenCount = countTokens(extracted.content);
|
|
166
167
|
if (tokenCount > maxOutputTokensArg)
|
|
167
168
|
return false;
|
|
@@ -176,7 +177,7 @@ async function outputSummaryFromExtractedContent({ ctx, url, extracted, extracti
|
|
|
176
177
|
const finishReport = flags.shouldComputeReport ? await hooks.buildReport() : null;
|
|
177
178
|
const payload = {
|
|
178
179
|
input: {
|
|
179
|
-
kind:
|
|
180
|
+
kind: "url",
|
|
180
181
|
url,
|
|
181
182
|
timeoutMs: flags.timeoutMs,
|
|
182
183
|
youtube: flags.youtubeMode,
|
|
@@ -184,9 +185,9 @@ async function outputSummaryFromExtractedContent({ ctx, url, extracted, extracti
|
|
|
184
185
|
format: flags.format,
|
|
185
186
|
markdown: effectiveMarkdownMode,
|
|
186
187
|
timestamps: flags.transcriptTimestamps,
|
|
187
|
-
length: flags.lengthArg.kind ===
|
|
188
|
-
? { kind:
|
|
189
|
-
: { kind:
|
|
188
|
+
length: flags.lengthArg.kind === "preset"
|
|
189
|
+
? { kind: "preset", preset: flags.lengthArg.preset }
|
|
190
|
+
: { kind: "chars", maxCharacters: flags.lengthArg.maxCharacters },
|
|
190
191
|
maxOutputTokens: flags.maxOutputTokensArg,
|
|
191
192
|
model: model.requestedModelLabel,
|
|
192
193
|
language: formatOutputLanguageForJson(flags.outputLanguage),
|
|
@@ -258,17 +259,17 @@ const pickModelForFinishLine = (llmCalls, fallback) => {
|
|
|
258
259
|
}
|
|
259
260
|
return null;
|
|
260
261
|
};
|
|
261
|
-
return (findLastModel(
|
|
262
|
-
findLastModel(
|
|
262
|
+
return (findLastModel("summary") ??
|
|
263
|
+
findLastModel("markdown") ??
|
|
263
264
|
(llmCalls.length > 0 ? (llmCalls[llmCalls.length - 1]?.model ?? null) : null) ??
|
|
264
265
|
fallback);
|
|
265
266
|
};
|
|
266
267
|
const buildModelMetaFromAttempt = (attempt) => {
|
|
267
|
-
if (attempt.transport ===
|
|
268
|
-
return { provider:
|
|
268
|
+
if (attempt.transport === "cli") {
|
|
269
|
+
return { provider: "cli", canonical: attempt.userModelId };
|
|
269
270
|
}
|
|
270
271
|
const parsed = parseGatewayStyleModelId(attempt.llmModelId ?? attempt.userModelId);
|
|
271
|
-
const canonical = attempt.userModelId.toLowerCase().startsWith(
|
|
272
|
+
const canonical = attempt.userModelId.toLowerCase().startsWith("openrouter/")
|
|
272
273
|
? attempt.userModelId
|
|
273
274
|
: parsed.canonical;
|
|
274
275
|
return { provider: parsed.provider, canonical };
|
|
@@ -280,14 +281,14 @@ export async function outputExtractedUrl({ ctx, url, extracted, extractionUi, pr
|
|
|
280
281
|
extracted: { diagnostics: extracted.diagnostics },
|
|
281
282
|
format: flags.format,
|
|
282
283
|
markdownMode: effectiveMarkdownMode,
|
|
283
|
-
hasMarkdownLlmCall: model.llmCalls.some((call) => call.purpose ===
|
|
284
|
+
hasMarkdownLlmCall: model.llmCalls.some((call) => call.purpose === "markdown"),
|
|
284
285
|
});
|
|
285
286
|
const finishModel = pickModelForFinishLine(model.llmCalls, null);
|
|
286
287
|
if (flags.json) {
|
|
287
288
|
const finishReport = flags.shouldComputeReport ? await hooks.buildReport() : null;
|
|
288
289
|
const payload = {
|
|
289
290
|
input: {
|
|
290
|
-
kind:
|
|
291
|
+
kind: "url",
|
|
291
292
|
url,
|
|
292
293
|
timeoutMs: flags.timeoutMs,
|
|
293
294
|
youtube: flags.youtubeMode,
|
|
@@ -295,9 +296,9 @@ export async function outputExtractedUrl({ ctx, url, extracted, extractionUi, pr
|
|
|
295
296
|
format: flags.format,
|
|
296
297
|
markdown: effectiveMarkdownMode,
|
|
297
298
|
timestamps: flags.transcriptTimestamps,
|
|
298
|
-
length: flags.lengthArg.kind ===
|
|
299
|
-
? { kind:
|
|
300
|
-
: { kind:
|
|
299
|
+
length: flags.lengthArg.kind === "preset"
|
|
300
|
+
? { kind: "preset", preset: flags.lengthArg.preset }
|
|
301
|
+
: { kind: "chars", maxCharacters: flags.lengthArg.maxCharacters },
|
|
301
302
|
maxOutputTokens: flags.maxOutputTokensArg,
|
|
302
303
|
model: model.requestedModelLabel,
|
|
303
304
|
language: formatOutputLanguageForJson(flags.outputLanguage),
|
|
@@ -345,12 +346,12 @@ export async function outputExtractedUrl({ ctx, url, extracted, extractionUi, pr
|
|
|
345
346
|
const extractCandidate = flags.transcriptTimestamps &&
|
|
346
347
|
extracted.transcriptTimedText &&
|
|
347
348
|
extracted.transcriptSource &&
|
|
348
|
-
extracted.content.toLowerCase().startsWith(
|
|
349
|
+
extracted.content.toLowerCase().startsWith("transcript:")
|
|
349
350
|
? `Transcript:\n${extracted.transcriptTimedText}`
|
|
350
351
|
: extracted.content;
|
|
351
352
|
const slideTags = slides?.slides && slides.slides.length > 0
|
|
352
|
-
? slides.slides.map((slide) => `[slide:${slide.index}]`).join(
|
|
353
|
-
:
|
|
353
|
+
? slides.slides.map((slide) => `[slide:${slide.index}]`).join("\n")
|
|
354
|
+
: "";
|
|
354
355
|
if (slidesOutput && slides?.slides && slides.slides.length > 0) {
|
|
355
356
|
const transcriptText = extracted.transcriptTimedText
|
|
356
357
|
? `Transcript:\n${extracted.transcriptTimedText}`
|
|
@@ -390,7 +391,7 @@ export async function outputExtractedUrl({ ctx, url, extracted, extractionUi, pr
|
|
|
390
391
|
}
|
|
391
392
|
return;
|
|
392
393
|
}
|
|
393
|
-
const renderedExtract = flags.format ===
|
|
394
|
+
const renderedExtract = flags.format === "markdown" && !flags.plain && isRichTty(io.stdout)
|
|
394
395
|
? renderMarkdownAnsi(prepareMarkdownForTerminal(extractCandidate), {
|
|
395
396
|
width: markdownRenderWidth(io.stdout, io.env),
|
|
396
397
|
wrap: true,
|
|
@@ -398,14 +399,14 @@ export async function outputExtractedUrl({ ctx, url, extracted, extractionUi, pr
|
|
|
398
399
|
hyperlinks: true,
|
|
399
400
|
})
|
|
400
401
|
: extractCandidate;
|
|
401
|
-
if (flags.format ===
|
|
402
|
-
io.stdout.write(`\n${renderedExtract.replace(/^\n+/,
|
|
402
|
+
if (flags.format === "markdown" && !flags.plain && isRichTty(io.stdout)) {
|
|
403
|
+
io.stdout.write(`\n${renderedExtract.replace(/^\n+/, "")}`);
|
|
403
404
|
}
|
|
404
405
|
else {
|
|
405
406
|
io.stdout.write(renderedExtract);
|
|
406
407
|
}
|
|
407
|
-
if (!renderedExtract.endsWith(
|
|
408
|
-
io.stdout.write(
|
|
408
|
+
if (!renderedExtract.endsWith("\n")) {
|
|
409
|
+
io.stdout.write("\n");
|
|
409
410
|
}
|
|
410
411
|
hooks.restoreProgressAfterStdout?.();
|
|
411
412
|
const slideFooter = slides ? [`slides ${slides.slides.length}`] : [];
|
|
@@ -434,9 +435,12 @@ export async function outputExtractedUrl({ ctx, url, extracted, extractionUi, pr
|
|
|
434
435
|
}
|
|
435
436
|
export async function summarizeExtractedUrl({ ctx, url, extracted, extractionUi, prompt, effectiveMarkdownMode, transcriptionCostLabel, onModelChosen, slides, slidesOutput, }) {
|
|
436
437
|
const { io, flags, model, cache: cacheState, hooks } = ctx;
|
|
438
|
+
const lastSuccessfulCliProvider = model.isFallbackModel
|
|
439
|
+
? await readLastSuccessfulCliProvider(io.envForRun)
|
|
440
|
+
: null;
|
|
437
441
|
const promptPayload = { system: SUMMARY_SYSTEM_PROMPT, userText: prompt };
|
|
438
442
|
const promptTokens = countTokens(promptPayload.userText);
|
|
439
|
-
const kindForAuto = extracted.siteName ===
|
|
443
|
+
const kindForAuto = extracted.siteName === "YouTube" ? "youtube" : "website";
|
|
440
444
|
const attempts = await (async () => {
|
|
441
445
|
if (model.isFallbackModel) {
|
|
442
446
|
const catalog = await model.getLiteLlmCatalog();
|
|
@@ -450,6 +454,9 @@ export async function summarizeExtractedUrl({ ctx, url, extracted, extractionUi,
|
|
|
450
454
|
catalog,
|
|
451
455
|
openrouterProvidersFromEnv: null,
|
|
452
456
|
cliAvailability: model.cliAvailability,
|
|
457
|
+
isImplicitAutoSelection: model.isImplicitAutoSelection,
|
|
458
|
+
allowAutoCliFallback: model.allowAutoCliFallback,
|
|
459
|
+
lastSuccessfulCliProvider,
|
|
453
460
|
});
|
|
454
461
|
if (flags.verbose) {
|
|
455
462
|
for (const attempt of list.slice(0, 8)) {
|
|
@@ -457,7 +464,7 @@ export async function summarizeExtractedUrl({ ctx, url, extracted, extractionUi,
|
|
|
457
464
|
}
|
|
458
465
|
}
|
|
459
466
|
return list.map((attempt) => {
|
|
460
|
-
if (attempt.transport !==
|
|
467
|
+
if (attempt.transport !== "cli")
|
|
461
468
|
return model.summaryEngine.applyZaiOverrides(attempt);
|
|
462
469
|
const parsed = parseCliUserModelId(attempt.userModelId);
|
|
463
470
|
return { ...attempt, cliProvider: parsed.provider, cliModel: parsed.model };
|
|
@@ -465,12 +472,12 @@ export async function summarizeExtractedUrl({ ctx, url, extracted, extractionUi,
|
|
|
465
472
|
}
|
|
466
473
|
/* v8 ignore next */
|
|
467
474
|
if (!model.fixedModelSpec) {
|
|
468
|
-
throw new Error(
|
|
475
|
+
throw new Error("Internal error: missing fixed model spec");
|
|
469
476
|
}
|
|
470
|
-
if (model.fixedModelSpec.transport ===
|
|
477
|
+
if (model.fixedModelSpec.transport === "cli") {
|
|
471
478
|
return [
|
|
472
479
|
{
|
|
473
|
-
transport:
|
|
480
|
+
transport: "cli",
|
|
474
481
|
userModelId: model.fixedModelSpec.userModelId,
|
|
475
482
|
llmModelId: null,
|
|
476
483
|
cliProvider: model.fixedModelSpec.cliProvider,
|
|
@@ -481,7 +488,7 @@ export async function summarizeExtractedUrl({ ctx, url, extracted, extractionUi,
|
|
|
481
488
|
},
|
|
482
489
|
];
|
|
483
490
|
}
|
|
484
|
-
const openaiOverrides = model.fixedModelSpec.requiredEnv ===
|
|
491
|
+
const openaiOverrides = model.fixedModelSpec.requiredEnv === "Z_AI_API_KEY"
|
|
485
492
|
? {
|
|
486
493
|
openaiApiKeyOverride: model.apiStatus.zaiApiKey,
|
|
487
494
|
openaiBaseUrlOverride: model.apiStatus.zaiBaseUrl,
|
|
@@ -490,7 +497,7 @@ export async function summarizeExtractedUrl({ ctx, url, extracted, extractionUi,
|
|
|
490
497
|
: {};
|
|
491
498
|
return [
|
|
492
499
|
{
|
|
493
|
-
transport: model.fixedModelSpec.transport ===
|
|
500
|
+
transport: model.fixedModelSpec.transport === "openrouter" ? "openrouter" : "native",
|
|
494
501
|
userModelId: model.fixedModelSpec.userModelId,
|
|
495
502
|
llmModelId: model.fixedModelSpec.llmModelId,
|
|
496
503
|
openrouterProviders: model.fixedModelSpec.openrouterProviders,
|
|
@@ -500,26 +507,29 @@ export async function summarizeExtractedUrl({ ctx, url, extracted, extractionUi,
|
|
|
500
507
|
},
|
|
501
508
|
];
|
|
502
509
|
})();
|
|
503
|
-
const cacheStore = cacheState.mode ===
|
|
510
|
+
const cacheStore = cacheState.mode === "default" && !flags.summaryCacheBypass ? cacheState.store : null;
|
|
504
511
|
const contentHash = cacheStore ? hashString(normalizeContentForHash(extracted.content)) : null;
|
|
505
512
|
const promptHash = cacheStore ? buildPromptHash(prompt) : null;
|
|
506
513
|
const lengthKey = buildLengthKey(flags.lengthArg);
|
|
507
514
|
const languageKey = buildLanguageKey(flags.outputLanguage);
|
|
515
|
+
const autoSelectionCacheModel = model.isFallbackModel
|
|
516
|
+
? `selection:${model.requestedModelInput.toLowerCase()}`
|
|
517
|
+
: null;
|
|
508
518
|
let summaryResult = null;
|
|
509
519
|
let usedAttempt = null;
|
|
510
520
|
let summaryFromCache = false;
|
|
511
521
|
let cacheChecked = false;
|
|
512
|
-
const isTweet = extracted.siteName?.toLowerCase() ===
|
|
513
|
-
const isYouTube = extracted.siteName ===
|
|
522
|
+
const isTweet = extracted.siteName?.toLowerCase() === "x" || isTwitterStatusUrl(extracted.url);
|
|
523
|
+
const isYouTube = extracted.siteName === "YouTube" || isYouTubeUrl(url);
|
|
514
524
|
const hasMedia = Boolean(extracted.video) ||
|
|
515
|
-
(extracted.transcriptSource != null && extracted.transcriptSource !==
|
|
516
|
-
(typeof extracted.mediaDurationSeconds ===
|
|
525
|
+
(extracted.transcriptSource != null && extracted.transcriptSource !== "unavailable") ||
|
|
526
|
+
(typeof extracted.mediaDurationSeconds === "number" && extracted.mediaDurationSeconds > 0) ||
|
|
517
527
|
extracted.isVideoOnly === true;
|
|
518
528
|
const autoBypass = ctx.model.isFallbackModel && !ctx.model.isNamedModelSelection;
|
|
519
529
|
const canBypassShortContent = (autoBypass || isTweet) &&
|
|
520
530
|
!flags.slides &&
|
|
521
531
|
!hasMedia &&
|
|
522
|
-
flags.streamMode !==
|
|
532
|
+
flags.streamMode !== "on" &&
|
|
523
533
|
!isYouTube &&
|
|
524
534
|
shouldBypassShortContentSummary({
|
|
525
535
|
extracted,
|
|
@@ -538,41 +548,78 @@ export async function summarizeExtractedUrl({ ctx, url, extracted, extractionUi,
|
|
|
538
548
|
effectiveMarkdownMode,
|
|
539
549
|
transcriptionCostLabel,
|
|
540
550
|
slides,
|
|
541
|
-
footerLabel:
|
|
542
|
-
verboseMessage:
|
|
551
|
+
footerLabel: "short content",
|
|
552
|
+
verboseMessage: "short content: skipping summary",
|
|
543
553
|
});
|
|
544
554
|
return;
|
|
545
555
|
}
|
|
546
556
|
if (cacheStore && contentHash && promptHash) {
|
|
547
557
|
cacheChecked = true;
|
|
548
|
-
|
|
549
|
-
if (!model.summaryEngine.envHasKeyFor(attempt.requiredEnv))
|
|
550
|
-
continue;
|
|
558
|
+
if (autoSelectionCacheModel) {
|
|
551
559
|
const key = buildSummaryCacheKey({
|
|
552
560
|
contentHash,
|
|
553
561
|
promptHash,
|
|
554
|
-
model:
|
|
562
|
+
model: autoSelectionCacheModel,
|
|
555
563
|
lengthKey,
|
|
556
564
|
languageKey,
|
|
557
565
|
});
|
|
558
|
-
const cached = cacheStore.
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
566
|
+
const cached = cacheStore.getJson("summary", key);
|
|
567
|
+
const cachedSummary = cached && typeof cached.summary === "string" ? cached.summary.trim() : null;
|
|
568
|
+
const cachedModelId = cached && typeof cached.model === "string" ? cached.model.trim() : null;
|
|
569
|
+
if (cachedSummary) {
|
|
570
|
+
const cachedAttempt = cachedModelId
|
|
571
|
+
? (attempts.find((attempt) => attempt.userModelId === cachedModelId) ?? null)
|
|
572
|
+
: null;
|
|
573
|
+
const fallbackAttempt = attempts.find((attempt) => model.summaryEngine.envHasKeyFor(attempt.requiredEnv)) ??
|
|
574
|
+
attempts[0] ??
|
|
575
|
+
null;
|
|
576
|
+
const matchedAttempt = cachedAttempt && model.summaryEngine.envHasKeyFor(cachedAttempt.requiredEnv)
|
|
577
|
+
? cachedAttempt
|
|
578
|
+
: fallbackAttempt;
|
|
579
|
+
if (matchedAttempt) {
|
|
580
|
+
writeVerbose(io.stderr, flags.verbose, "cache hit summary (auto selection)", flags.verboseColor, io.envForRun);
|
|
581
|
+
onModelChosen?.(cachedModelId || matchedAttempt.userModelId);
|
|
582
|
+
summaryResult = {
|
|
583
|
+
summary: cachedSummary,
|
|
584
|
+
summaryAlreadyPrinted: false,
|
|
585
|
+
modelMeta: buildModelMetaFromAttempt(matchedAttempt),
|
|
586
|
+
maxOutputTokensForCall: null,
|
|
587
|
+
};
|
|
588
|
+
usedAttempt = matchedAttempt;
|
|
589
|
+
summaryFromCache = true;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
if (!summaryFromCache) {
|
|
594
|
+
for (const attempt of attempts) {
|
|
595
|
+
if (!model.summaryEngine.envHasKeyFor(attempt.requiredEnv))
|
|
596
|
+
continue;
|
|
597
|
+
const key = buildSummaryCacheKey({
|
|
598
|
+
contentHash,
|
|
599
|
+
promptHash,
|
|
600
|
+
model: attempt.userModelId,
|
|
601
|
+
lengthKey,
|
|
602
|
+
languageKey,
|
|
603
|
+
});
|
|
604
|
+
const cached = cacheStore.getText("summary", key);
|
|
605
|
+
if (!cached)
|
|
606
|
+
continue;
|
|
607
|
+
writeVerbose(io.stderr, flags.verbose, "cache hit summary", flags.verboseColor, io.envForRun);
|
|
608
|
+
onModelChosen?.(attempt.userModelId);
|
|
609
|
+
summaryResult = {
|
|
610
|
+
summary: cached,
|
|
611
|
+
summaryAlreadyPrinted: false,
|
|
612
|
+
modelMeta: buildModelMetaFromAttempt(attempt),
|
|
613
|
+
maxOutputTokensForCall: null,
|
|
614
|
+
};
|
|
615
|
+
usedAttempt = attempt;
|
|
616
|
+
summaryFromCache = true;
|
|
617
|
+
break;
|
|
618
|
+
}
|
|
572
619
|
}
|
|
573
620
|
}
|
|
574
621
|
if (cacheChecked && !summaryFromCache) {
|
|
575
|
-
writeVerbose(io.stderr, flags.verbose,
|
|
622
|
+
writeVerbose(io.stderr, flags.verbose, "cache miss summary", flags.verboseColor, io.envForRun);
|
|
576
623
|
}
|
|
577
624
|
ctx.hooks.onSummaryCached?.(summaryFromCache);
|
|
578
625
|
let lastError = null;
|
|
@@ -618,7 +665,7 @@ export async function summarizeExtractedUrl({ ctx, url, extracted, extractionUi,
|
|
|
618
665
|
};
|
|
619
666
|
if (model.isNamedModelSelection) {
|
|
620
667
|
if (lastError === null && missingRequiredEnvs.size > 0) {
|
|
621
|
-
throw new Error(withFreeTip(`Missing ${Array.from(missingRequiredEnvs).sort().join(
|
|
668
|
+
throw new Error(withFreeTip(`Missing ${Array.from(missingRequiredEnvs).sort().join(", ")} for --model ${model.requestedModelInput}.`));
|
|
622
669
|
}
|
|
623
670
|
if (lastError instanceof Error) {
|
|
624
671
|
if (sawOpenRouterNoAllowedProviders) {
|
|
@@ -642,21 +689,41 @@ export async function summarizeExtractedUrl({ ctx, url, extracted, extractionUi,
|
|
|
642
689
|
effectiveMarkdownMode,
|
|
643
690
|
transcriptionCostLabel,
|
|
644
691
|
slides,
|
|
645
|
-
footerLabel:
|
|
692
|
+
footerLabel: "no model",
|
|
646
693
|
verboseMessage: lastError instanceof Error ? `auto failed all models: ${lastError.message}` : null,
|
|
647
694
|
});
|
|
648
695
|
return;
|
|
649
696
|
}
|
|
650
697
|
if (!summaryFromCache && cacheStore && contentHash && promptHash) {
|
|
651
|
-
const
|
|
698
|
+
const perModelKey = buildSummaryCacheKey({
|
|
652
699
|
contentHash,
|
|
653
700
|
promptHash,
|
|
654
701
|
model: usedAttempt.userModelId,
|
|
655
702
|
lengthKey,
|
|
656
703
|
languageKey,
|
|
657
704
|
});
|
|
658
|
-
cacheStore.setText(
|
|
659
|
-
writeVerbose(io.stderr, flags.verbose,
|
|
705
|
+
cacheStore.setText("summary", perModelKey, summaryResult.summary, cacheState.ttlMs);
|
|
706
|
+
writeVerbose(io.stderr, flags.verbose, "cache write summary", flags.verboseColor, io.envForRun);
|
|
707
|
+
if (autoSelectionCacheModel) {
|
|
708
|
+
const selectionKey = buildSummaryCacheKey({
|
|
709
|
+
contentHash,
|
|
710
|
+
promptHash,
|
|
711
|
+
model: autoSelectionCacheModel,
|
|
712
|
+
lengthKey,
|
|
713
|
+
languageKey,
|
|
714
|
+
});
|
|
715
|
+
cacheStore.setJson("summary", selectionKey, { summary: summaryResult.summary, model: usedAttempt.userModelId }, cacheState.ttlMs);
|
|
716
|
+
writeVerbose(io.stderr, flags.verbose, "cache write summary (auto selection)", flags.verboseColor, io.envForRun);
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
if (!summaryFromCache &&
|
|
720
|
+
model.isFallbackModel &&
|
|
721
|
+
usedAttempt.transport === "cli" &&
|
|
722
|
+
usedAttempt.cliProvider) {
|
|
723
|
+
await writeLastSuccessfulCliProvider({
|
|
724
|
+
env: io.envForRun,
|
|
725
|
+
provider: usedAttempt.cliProvider,
|
|
726
|
+
});
|
|
660
727
|
}
|
|
661
728
|
const { summary, summaryAlreadyPrinted, modelMeta, maxOutputTokensForCall } = summaryResult;
|
|
662
729
|
const normalizedSummary = slides && slides.slides.length > 0 ? normalizeSummarySlideHeadings(summary) : summary;
|
|
@@ -664,7 +731,7 @@ export async function summarizeExtractedUrl({ ctx, url, extracted, extractionUi,
|
|
|
664
731
|
const finishReport = flags.shouldComputeReport ? await hooks.buildReport() : null;
|
|
665
732
|
const payload = {
|
|
666
733
|
input: {
|
|
667
|
-
kind:
|
|
734
|
+
kind: "url",
|
|
668
735
|
url,
|
|
669
736
|
timeoutMs: flags.timeoutMs,
|
|
670
737
|
youtube: flags.youtubeMode,
|
|
@@ -672,9 +739,9 @@ export async function summarizeExtractedUrl({ ctx, url, extracted, extractionUi,
|
|
|
672
739
|
format: flags.format,
|
|
673
740
|
markdown: effectiveMarkdownMode,
|
|
674
741
|
timestamps: flags.transcriptTimestamps,
|
|
675
|
-
length: flags.lengthArg.kind ===
|
|
676
|
-
? { kind:
|
|
677
|
-
: { kind:
|
|
742
|
+
length: flags.lengthArg.kind === "preset"
|
|
743
|
+
? { kind: "preset", preset: flags.lengthArg.preset }
|
|
744
|
+
: { kind: "chars", maxCharacters: flags.lengthArg.maxCharacters },
|
|
678
745
|
maxOutputTokens: flags.maxOutputTokensArg,
|
|
679
746
|
model: model.requestedModelLabel,
|
|
680
747
|
language: formatOutputLanguageForJson(flags.outputLanguage),
|
|
@@ -695,7 +762,7 @@ export async function summarizeExtractedUrl({ ctx, url, extracted, extractionUi,
|
|
|
695
762
|
provider: modelMeta.provider,
|
|
696
763
|
model: usedAttempt.userModelId,
|
|
697
764
|
maxCompletionTokens: maxOutputTokensForCall,
|
|
698
|
-
strategy:
|
|
765
|
+
strategy: "single",
|
|
699
766
|
},
|
|
700
767
|
metrics: flags.metricsEnabled ? finishReport : null,
|
|
701
768
|
summary: normalizedSummary,
|
|
@@ -707,7 +774,7 @@ export async function summarizeExtractedUrl({ ctx, url, extracted, extractionUi,
|
|
|
707
774
|
stderr: io.stderr,
|
|
708
775
|
env: io.envForRun,
|
|
709
776
|
elapsedMs: Date.now() - flags.runStartedAtMs,
|
|
710
|
-
elapsedLabel: summaryFromCache ?
|
|
777
|
+
elapsedLabel: summaryFromCache ? "Cached" : null,
|
|
711
778
|
label: extractionUi.finishSourceLabel,
|
|
712
779
|
model: usedAttempt.userModelId,
|
|
713
780
|
report: finishReport,
|
|
@@ -750,15 +817,15 @@ export async function summarizeExtractedUrl({ ctx, url, extracted, extractionUi,
|
|
|
750
817
|
})
|
|
751
818
|
: normalizedSummary;
|
|
752
819
|
if (!flags.plain && isRichTty(io.stdout)) {
|
|
753
|
-
io.stdout.write(`\n${rendered.replace(/^\n+/,
|
|
820
|
+
io.stdout.write(`\n${rendered.replace(/^\n+/, "")}`);
|
|
754
821
|
}
|
|
755
822
|
else {
|
|
756
823
|
if (isRichTty(io.stdout))
|
|
757
|
-
io.stdout.write(
|
|
758
|
-
io.stdout.write(rendered.replace(/^\n+/,
|
|
824
|
+
io.stdout.write("\n");
|
|
825
|
+
io.stdout.write(rendered.replace(/^\n+/, ""));
|
|
759
826
|
}
|
|
760
|
-
if (!rendered.endsWith(
|
|
761
|
-
io.stdout.write(
|
|
827
|
+
if (!rendered.endsWith("\n")) {
|
|
828
|
+
io.stdout.write("\n");
|
|
762
829
|
}
|
|
763
830
|
hooks.restoreProgressAfterStdout?.();
|
|
764
831
|
}
|
|
@@ -769,7 +836,7 @@ export async function summarizeExtractedUrl({ ctx, url, extracted, extractionUi,
|
|
|
769
836
|
stderr: io.stderr,
|
|
770
837
|
env: io.envForRun,
|
|
771
838
|
elapsedMs: Date.now() - flags.runStartedAtMs,
|
|
772
|
-
elapsedLabel: summaryFromCache ?
|
|
839
|
+
elapsedLabel: summaryFromCache ? "Cached" : null,
|
|
773
840
|
label: extractionUi.finishSourceLabel,
|
|
774
841
|
model: modelMeta.canonical,
|
|
775
842
|
report,
|