@nodetool-ai/runtime 0.8.2 → 0.8.4-a13.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/dist/.nodetool-build-stamp +1 -1
- package/dist/canvas-codec.d.ts +3 -0
- package/dist/canvas-codec.d.ts.map +1 -0
- package/dist/canvas-codec.js +12 -0
- package/dist/canvas-codec.js.map +1 -0
- package/dist/context-packer.d.ts +1 -1
- package/dist/context-packer.js +1 -1
- package/dist/context.d.ts +3 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +16 -5
- package/dist/context.js.map +1 -1
- package/dist/host-binaries.d.ts.map +1 -1
- package/dist/host-binaries.js +4 -3
- package/dist/host-binaries.js.map +1 -1
- package/dist/http-client-spans.d.ts +9 -0
- package/dist/http-client-spans.d.ts.map +1 -0
- package/dist/http-client-spans.js +88 -0
- package/dist/http-client-spans.js.map +1 -0
- package/dist/image-codec.d.ts +6 -3
- package/dist/image-codec.d.ts.map +1 -1
- package/dist/image-codec.js +17 -17
- package/dist/image-codec.js.map +1 -1
- package/dist/image-model-tasks.d.ts +20 -0
- package/dist/image-model-tasks.d.ts.map +1 -1
- package/dist/image-model-tasks.js +44 -1
- package/dist/image-model-tasks.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/media-ref-bytes.d.ts.map +1 -1
- package/dist/media-ref-bytes.js +31 -6
- package/dist/media-ref-bytes.js.map +1 -1
- package/dist/otlp-json-trace-exporter.d.ts +89 -0
- package/dist/otlp-json-trace-exporter.d.ts.map +1 -0
- package/dist/otlp-json-trace-exporter.js +155 -0
- package/dist/otlp-json-trace-exporter.js.map +1 -0
- package/dist/prompt-asset-refs.d.ts +12 -1
- package/dist/prompt-asset-refs.d.ts.map +1 -1
- package/dist/prompt-asset-refs.js +9 -4
- package/dist/prompt-asset-refs.js.map +1 -1
- package/dist/providers/antigravity-provider.d.ts +113 -0
- package/dist/providers/antigravity-provider.d.ts.map +1 -0
- package/dist/providers/antigravity-provider.js +527 -0
- package/dist/providers/antigravity-provider.js.map +1 -0
- package/dist/providers/api-route-provider.d.ts +22 -0
- package/dist/providers/api-route-provider.d.ts.map +1 -0
- package/dist/providers/api-route-provider.js +56 -0
- package/dist/providers/api-route-provider.js.map +1 -0
- package/dist/providers/azure-openai-provider.d.ts +69 -0
- package/dist/providers/azure-openai-provider.d.ts.map +1 -0
- package/dist/providers/azure-openai-provider.js +233 -0
- package/dist/providers/azure-openai-provider.js.map +1 -0
- package/dist/providers/base-provider.d.ts.map +1 -1
- package/dist/providers/base-provider.js +41 -4
- package/dist/providers/base-provider.js.map +1 -1
- package/dist/providers/bedrock-provider.d.ts +52 -0
- package/dist/providers/bedrock-provider.d.ts.map +1 -0
- package/dist/providers/bedrock-provider.js +648 -0
- package/dist/providers/bedrock-provider.js.map +1 -0
- package/dist/providers/bedrock-transport.d.ts +37 -0
- package/dist/providers/bedrock-transport.d.ts.map +1 -0
- package/dist/providers/bedrock-transport.js +186 -0
- package/dist/providers/bedrock-transport.js.map +1 -0
- package/dist/providers/chatgpt-provider.d.ts +48 -0
- package/dist/providers/chatgpt-provider.d.ts.map +1 -0
- package/dist/providers/chatgpt-provider.js +551 -0
- package/dist/providers/chatgpt-provider.js.map +1 -0
- package/dist/providers/claude-agent-provider.d.ts +29 -51
- package/dist/providers/claude-agent-provider.d.ts.map +1 -1
- package/dist/providers/claude-agent-provider.js +144 -202
- package/dist/providers/claude-agent-provider.js.map +1 -1
- package/dist/providers/claude-cli.d.ts +265 -0
- package/dist/providers/claude-cli.d.ts.map +1 -0
- package/dist/providers/claude-cli.js +488 -0
- package/dist/providers/claude-cli.js.map +1 -0
- package/dist/providers/codex-provider.d.ts +9 -2
- package/dist/providers/codex-provider.d.ts.map +1 -1
- package/dist/providers/codex-provider.js +25 -11
- package/dist/providers/codex-provider.js.map +1 -1
- package/dist/providers/context-window.d.ts.map +1 -1
- package/dist/providers/context-window.js +1 -0
- package/dist/providers/context-window.js.map +1 -1
- package/dist/providers/core-tools.d.ts +4 -1
- package/dist/providers/core-tools.d.ts.map +1 -1
- package/dist/providers/core-tools.js +5 -3
- package/dist/providers/core-tools.js.map +1 -1
- package/dist/providers/cost-calculator.d.ts.map +1 -1
- package/dist/providers/cost-calculator.js +9 -0
- package/dist/providers/cost-calculator.js.map +1 -1
- package/dist/providers/credential-check.d.ts +8 -0
- package/dist/providers/credential-check.d.ts.map +1 -1
- package/dist/providers/credential-check.js +22 -0
- package/dist/providers/credential-check.js.map +1 -1
- package/dist/providers/custom-openai-provider.d.ts.map +1 -1
- package/dist/providers/custom-openai-provider.js +19 -1
- package/dist/providers/custom-openai-provider.js.map +1 -1
- package/dist/providers/dreamina-pending.d.ts +22 -0
- package/dist/providers/dreamina-pending.d.ts.map +1 -0
- package/dist/providers/dreamina-pending.js +86 -0
- package/dist/providers/dreamina-pending.js.map +1 -0
- package/dist/providers/dreamina-provider.d.ts +10 -3
- package/dist/providers/dreamina-provider.d.ts.map +1 -1
- package/dist/providers/dreamina-provider.js +325 -45
- package/dist/providers/dreamina-provider.js.map +1 -1
- package/dist/providers/extension-tab.d.ts +42 -0
- package/dist/providers/extension-tab.d.ts.map +1 -0
- package/dist/providers/extension-tab.js +200 -0
- package/dist/providers/extension-tab.js.map +1 -0
- package/dist/providers/fake-provider.d.ts +3 -1
- package/dist/providers/fake-provider.d.ts.map +1 -1
- package/dist/providers/fake-provider.js +63 -9
- package/dist/providers/fake-provider.js.map +1 -1
- package/dist/providers/fake-schema-answer.d.ts +10 -0
- package/dist/providers/fake-schema-answer.d.ts.map +1 -0
- package/dist/providers/fake-schema-answer.js +64 -0
- package/dist/providers/fake-schema-answer.js.map +1 -0
- package/dist/providers/fal-provider.d.ts.map +1 -1
- package/dist/providers/fal-provider.js +5 -2
- package/dist/providers/fal-provider.js.map +1 -1
- package/dist/providers/gemini-web-provider.d.ts +76 -0
- package/dist/providers/gemini-web-provider.d.ts.map +1 -0
- package/dist/providers/gemini-web-provider.js +827 -0
- package/dist/providers/gemini-web-provider.js.map +1 -0
- package/dist/providers/http-transport.d.ts.map +1 -1
- package/dist/providers/http-transport.js +2 -0
- package/dist/providers/http-transport.js.map +1 -1
- package/dist/providers/huggingface-provider.d.ts.map +1 -1
- package/dist/providers/huggingface-provider.js +17 -1
- package/dist/providers/huggingface-provider.js.map +1 -1
- package/dist/providers/index.d.ts +18 -2
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +66 -15
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/llama-provider.d.ts +8 -0
- package/dist/providers/llama-provider.d.ts.map +1 -1
- package/dist/providers/llama-provider.js +23 -5
- package/dist/providers/llama-provider.js.map +1 -1
- package/dist/providers/lmstudio-provider.js +2 -2
- package/dist/providers/lmstudio-provider.js.map +1 -1
- package/dist/providers/manifest-models.d.ts +2 -12
- package/dist/providers/manifest-models.d.ts.map +1 -1
- package/dist/providers/manifest-models.js +45 -3
- package/dist/providers/manifest-models.js.map +1 -1
- package/dist/providers/meta-provider.d.ts +53 -6
- package/dist/providers/meta-provider.d.ts.map +1 -1
- package/dist/providers/meta-provider.js +289 -6
- package/dist/providers/meta-provider.js.map +1 -1
- package/dist/providers/meta-segmentation.d.ts +27 -0
- package/dist/providers/meta-segmentation.d.ts.map +1 -0
- package/dist/providers/meta-segmentation.js +141 -0
- package/dist/providers/meta-segmentation.js.map +1 -0
- package/dist/providers/meta-transcription.d.ts +31 -0
- package/dist/providers/meta-transcription.d.ts.map +1 -0
- package/dist/providers/meta-transcription.js +187 -0
- package/dist/providers/meta-transcription.js.map +1 -0
- package/dist/providers/node-llama-cpp-provider.d.ts +6 -3
- package/dist/providers/node-llama-cpp-provider.d.ts.map +1 -1
- package/dist/providers/node-llama-cpp-provider.js +77 -62
- package/dist/providers/node-llama-cpp-provider.js.map +1 -1
- package/dist/providers/ollama-provider.d.ts +23 -0
- package/dist/providers/ollama-provider.d.ts.map +1 -1
- package/dist/providers/ollama-provider.js +140 -27
- package/dist/providers/ollama-provider.js.map +1 -1
- package/dist/providers/openai-compat/client.d.ts +10 -1
- package/dist/providers/openai-compat/client.d.ts.map +1 -1
- package/dist/providers/openai-compat/client.js +21 -3
- package/dist/providers/openai-compat/client.js.map +1 -1
- package/dist/providers/openai-compat/index.d.ts +1 -1
- package/dist/providers/openai-compat/index.d.ts.map +1 -1
- package/dist/providers/openai-compat/index.js +1 -1
- package/dist/providers/openai-compat/index.js.map +1 -1
- package/dist/providers/openai-compat/types.d.ts +4 -0
- package/dist/providers/openai-compat/types.d.ts.map +1 -1
- package/dist/providers/openai-compat-provider.d.ts +11 -0
- package/dist/providers/openai-compat-provider.d.ts.map +1 -1
- package/dist/providers/openai-compat-provider.js +47 -2
- package/dist/providers/openai-compat-provider.js.map +1 -1
- package/dist/providers/openai-provider.d.ts +15 -0
- package/dist/providers/openai-provider.d.ts.map +1 -1
- package/dist/providers/openai-provider.js +50 -12
- package/dist/providers/openai-provider.js.map +1 -1
- package/dist/providers/opper-provider.d.ts +31 -0
- package/dist/providers/opper-provider.d.ts.map +1 -0
- package/dist/providers/opper-provider.js +91 -0
- package/dist/providers/opper-provider.js.map +1 -0
- package/dist/providers/provider-request-log.d.ts.map +1 -1
- package/dist/providers/provider-request-log.js +3 -2
- package/dist/providers/provider-request-log.js.map +1 -1
- package/dist/providers/python-provider.d.ts.map +1 -1
- package/dist/providers/python-provider.js +5 -3
- package/dist/providers/python-provider.js.map +1 -1
- package/dist/providers/responses-api.d.ts +12 -2
- package/dist/providers/responses-api.d.ts.map +1 -1
- package/dist/providers/responses-api.js +54 -3
- package/dist/providers/responses-api.js.map +1 -1
- package/dist/providers/safe-url.d.ts.map +1 -1
- package/dist/providers/safe-url.js +8 -0
- package/dist/providers/safe-url.js.map +1 -1
- package/dist/providers/scripted-provider.d.ts.map +1 -1
- package/dist/providers/scripted-provider.js +6 -1
- package/dist/providers/scripted-provider.js.map +1 -1
- package/dist/providers/types.d.ts +16 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/types.js.map +1 -1
- package/dist/providers/vgenv-provider.d.ts +33 -0
- package/dist/providers/vgenv-provider.d.ts.map +1 -0
- package/dist/providers/vgenv-provider.js +261 -0
- package/dist/providers/vgenv-provider.js.map +1 -0
- package/dist/providers/vllm-provider.d.ts +7 -0
- package/dist/providers/vllm-provider.d.ts.map +1 -1
- package/dist/providers/vllm-provider.js +15 -3
- package/dist/providers/vllm-provider.js.map +1 -1
- package/dist/python-bridge-base.d.ts +16 -6
- package/dist/python-bridge-base.d.ts.map +1 -1
- package/dist/python-bridge-base.js +117 -40
- package/dist/python-bridge-base.js.map +1 -1
- package/dist/python-bridge-codec.d.ts +16 -1
- package/dist/python-bridge-codec.d.ts.map +1 -1
- package/dist/python-bridge-codec.js +60 -2
- package/dist/python-bridge-codec.js.map +1 -1
- package/dist/python-bridge-framing.d.ts +10 -1
- package/dist/python-bridge-framing.d.ts.map +1 -1
- package/dist/python-bridge-framing.js +33 -8
- package/dist/python-bridge-framing.js.map +1 -1
- package/dist/python-bridge-types.d.ts +26 -5
- package/dist/python-bridge-types.d.ts.map +1 -1
- package/dist/python-bridge-types.js.map +1 -1
- package/dist/python-graph-resolver.d.ts.map +1 -1
- package/dist/python-graph-resolver.js +12 -2
- package/dist/python-graph-resolver.js.map +1 -1
- package/dist/python-node-executor.d.ts +14 -3
- package/dist/python-node-executor.d.ts.map +1 -1
- package/dist/python-node-executor.js +185 -3
- package/dist/python-node-executor.js.map +1 -1
- package/dist/python-stdio-bridge.d.ts +6 -0
- package/dist/python-stdio-bridge.d.ts.map +1 -1
- package/dist/python-stdio-bridge.js +58 -12
- package/dist/python-stdio-bridge.js.map +1 -1
- package/dist/run-trace-context.d.ts +2 -27
- package/dist/run-trace-context.d.ts.map +1 -1
- package/dist/run-trace-context.js +4 -51
- package/dist/run-trace-context.js.map +1 -1
- package/dist/run-trace-processor.d.ts +3 -0
- package/dist/run-trace-processor.d.ts.map +1 -1
- package/dist/run-trace-processor.js +63 -46
- package/dist/run-trace-processor.js.map +1 -1
- package/dist/run-trace-scope.d.ts +28 -0
- package/dist/run-trace-scope.d.ts.map +1 -0
- package/dist/run-trace-scope.js +51 -0
- package/dist/run-trace-scope.js.map +1 -0
- package/dist/run-trace-serialization.d.ts.map +1 -1
- package/dist/run-trace-serialization.js +4 -3
- package/dist/run-trace-serialization.js.map +1 -1
- package/dist/span-record.d.ts +4 -0
- package/dist/span-record.d.ts.map +1 -0
- package/dist/span-record.js +48 -0
- package/dist/span-record.js.map +1 -0
- package/dist/swappable-python-bridge.d.ts +6 -6
- package/dist/swappable-python-bridge.d.ts.map +1 -1
- package/dist/swappable-python-bridge.js +10 -10
- package/dist/swappable-python-bridge.js.map +1 -1
- package/dist/telemetry.d.ts +2 -2
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +33 -34
- package/dist/telemetry.js.map +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +55 -3
- package/dist/testing.js.map +1 -1
- package/dist/token-counter.d.ts +4 -13
- package/dist/token-counter.d.ts.map +1 -1
- package/dist/token-counter.js +68 -18
- package/dist/token-counter.js.map +1 -1
- package/dist/trace-context-manager.d.ts +12 -0
- package/dist/trace-context-manager.d.ts.map +1 -0
- package/dist/trace-context-manager.js +30 -0
- package/dist/trace-context-manager.js.map +1 -0
- package/dist/trace-exporters.d.ts +2 -3
- package/dist/trace-exporters.d.ts.map +1 -1
- package/dist/trace-exporters.js +5 -52
- package/dist/trace-exporters.js.map +1 -1
- package/dist/trace-semantic-conventions.d.ts +2 -0
- package/dist/trace-semantic-conventions.d.ts.map +1 -0
- package/dist/trace-semantic-conventions.js +2 -0
- package/dist/trace-semantic-conventions.js.map +1 -0
- package/dist/traced-storage.d.ts +17 -0
- package/dist/traced-storage.d.ts.map +1 -0
- package/dist/traced-storage.js +41 -0
- package/dist/traced-storage.js.map +1 -0
- package/dist/tracing-module.d.ts +3 -0
- package/dist/tracing-module.d.ts.map +1 -0
- package/dist/tracing-module.js +12 -0
- package/dist/tracing-module.js.map +1 -0
- package/package.json +37 -18
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dreamina-provider.d.ts","sourceRoot":"","sources":["../../src/providers/dreamina-provider.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAG3E,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"dreamina-provider.d.ts","sourceRoot":"","sources":["../../src/providers/dreamina-provider.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAG3E,OAAO,EAAmC,KAAK,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AACnG,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,UAAU,EAAuB,MAAM,YAAY,CAAC;AAI5R,qEAAqE;AACrE,MAAM,WAAW,kBAAkB;IACjC,8FAA8F;IAC9F,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAChD;AAkED,uEAAuE;AACvE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI,GAAG,IAAI,CAE7E;AAQD,kGAAkG;AAClG,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,oBAAoB,GAAG,IAAI,GAAG,IAAI,CAEhF;AA2yBD;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,YAAY;IAChD,OAAO,CAAC,WAAW,CAA8D;IACjF,OAAO,CAAC,gBAAgB,CAAmE;IAC3F,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA2H;IAEjJ,UAAmB,oBAAoB,IAAI,SAAS,kBAAkB,EAAE,CAEvE;IAED,OAAgB,eAAe,IAAI,MAAM,EAAE,CAAe;IAC1D,YAAY,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EAAmC;IAE/E,eAAe,CAAC,KAAK,EAAE,UAAU,CAAC,YAAY,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAkE;IAEzJ,gBAAgB,CAAC,KAAK,EAAE,UAAU,CAAC,YAAY,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,kBAAkB,CAAC,CAAkE;YAEvK,YAAY;IAQ1B,mFAAmF;IACpE,uBAAuB,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CAE9D;YAEa,WAAW;IAMzB,8EAA8E;IAC9E,OAAO,CAAC,SAAS;YAaH,WAAW;IAeV,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CAKzE;IAED;;;;;OAKG;IACY,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,CAAC,CAajG;YAEa,YAAY;IAQX,uBAAuB,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CAE9D;IAEc,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CAEzE;IAED;;;;;;OAMG;IACY,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,CAAC,CAE9F;IAED;;;;;;;OAOG;IACY,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,EAAE,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,UAAU,CAAC,CAOnH;IAEc,uBAAuB,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CAE9D;IAED,oGAAoG;IACrF,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAEjF;YAEa,aAAa;IA0F3B,2DAA2D;YAC7C,YAAY;CAW3B"}
|
|
@@ -6,6 +6,7 @@ import { fetchExternalMedia } from "../external-media-fetch.js";
|
|
|
6
6
|
import { BaseProvider } from "./base-provider.js";
|
|
7
7
|
import { createDreaminaMusic } from "./dreamina-music.js";
|
|
8
8
|
import { DREAMINA_IMAGE_MODELS, DREAMINA_VIDEO_MODELS } from "./dreamina-models.js";
|
|
9
|
+
import { filePendingStore, pendingJobKey } from "./dreamina-pending.js";
|
|
9
10
|
const log = createLogger("nodetool.runtime.providers.dreamina");
|
|
10
11
|
/** The extension keeps the debugger attached between polls, then detaches when idle. */
|
|
11
12
|
const IDLE_CLOSE_MS = 30_000;
|
|
@@ -76,6 +77,41 @@ export function setDreaminaPageRunner(runner) {
|
|
|
76
77
|
function runner() {
|
|
77
78
|
return (pageRunner ??= createExtensionRunner());
|
|
78
79
|
}
|
|
80
|
+
let pendingStore = null;
|
|
81
|
+
/** Replace the store of uncollected jobs. Tests use this to keep the data directory untouched. */
|
|
82
|
+
export function setDreaminaPendingStore(store) {
|
|
83
|
+
pendingStore = store;
|
|
84
|
+
}
|
|
85
|
+
function pendingJobs() {
|
|
86
|
+
return (pendingStore ??= filePendingStore());
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* A request to the tab that did not answer in time. Its outcome is unknown:
|
|
90
|
+
* for the generate request, Dreamina may already run (and bill) the job.
|
|
91
|
+
*/
|
|
92
|
+
class DreaminaStallError extends Error {
|
|
93
|
+
}
|
|
94
|
+
/** Whether `error` means a request stalled, so its outcome is unknown. */
|
|
95
|
+
function stalled(error) {
|
|
96
|
+
return error instanceof DreaminaStallError;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Evaluate `expression` in the tab, but stop waiting after `timeoutMs`. The
|
|
100
|
+
* provider keeps its own limit, so a stalled tab or bridge cannot hang a poll.
|
|
101
|
+
*/
|
|
102
|
+
async function evaluateBounded(expression, what, timeoutMs = TAB_CALL_TIMEOUT_MS) {
|
|
103
|
+
let timer;
|
|
104
|
+
const timeout = new Promise((_, reject) => {
|
|
105
|
+
timer = setTimeout(() => reject(new DreaminaStallError(`Dreamina tab did not answer ${what} within ${timeoutMs / 1000}s`)), timeoutMs);
|
|
106
|
+
timer.unref?.();
|
|
107
|
+
});
|
|
108
|
+
try {
|
|
109
|
+
return await Promise.race([runner().evaluate(expression), timeout]);
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
clearTimeout(timer);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
79
115
|
const API_BASE = "https://mweb-api-sg.capcut.com";
|
|
80
116
|
const APP_ID = "513641";
|
|
81
117
|
const APP_VERSION = "8.4.0";
|
|
@@ -83,9 +119,19 @@ const DA_VERSION = "3.3.28";
|
|
|
83
119
|
const WEB_VERSION = "7.5.0";
|
|
84
120
|
const POLL_INTERVAL_MS = 2500;
|
|
85
121
|
const MAX_POLL_FAILURES = 5;
|
|
122
|
+
/** How long a submit cut off by a lost tab has to show up in the history. */
|
|
123
|
+
const UNCONFIRMED_SUBMIT_GRACE_MS = 30_000;
|
|
124
|
+
const GENERATE_PATH = "/mweb/v1/aigc_draft/generate";
|
|
86
125
|
const DEFAULT_TIMEOUT_MS = 5 * 60_000;
|
|
87
|
-
|
|
126
|
+
/** Dreamina queues video for well over ten minutes at busy times. */
|
|
127
|
+
const VIDEO_TIMEOUT_MS = 30 * 60_000;
|
|
88
128
|
const CONFIG_TTL_MS = 5 * 60_000;
|
|
129
|
+
/** One API request in the tab gives up after this, so a stalled request cannot block a poll. */
|
|
130
|
+
const API_TIMEOUT_MS = 30_000;
|
|
131
|
+
/** The whole tab round trip, past the request's own limit, before the provider stops waiting. */
|
|
132
|
+
const TAB_CALL_TIMEOUT_MS = API_TIMEOUT_MS + 15_000;
|
|
133
|
+
/** One image download attempt gives up after this. */
|
|
134
|
+
const DOWNLOAD_TIMEOUT_MS = 60_000;
|
|
89
135
|
const VIDEO_FPS = 24;
|
|
90
136
|
/** Dreamina's `image_ratio` enum, in the order the web app numbers it. */
|
|
91
137
|
const RATIO_TYPES = {
|
|
@@ -125,7 +171,33 @@ function signedHeaders(path) {
|
|
|
125
171
|
appid: APP_ID
|
|
126
172
|
};
|
|
127
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* Whether `error` means the Dreamina tab went away: closed, reloaded or
|
|
176
|
+
* navigated while a command ran. The runner drops its attach on any error, so
|
|
177
|
+
* the next call attaches afresh.
|
|
178
|
+
*/
|
|
179
|
+
function tabLost(error) {
|
|
180
|
+
return error instanceof Error && /detached|target[ _]closed|no tab with/i.test(error.message);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Run `step` and, if the tab is lost, once more on a fresh attach. Only for
|
|
184
|
+
* steps that cost nothing and can repeat: never the generate request.
|
|
185
|
+
*/
|
|
186
|
+
async function retryOnTabLoss(what, step) {
|
|
187
|
+
try {
|
|
188
|
+
return await step();
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
if (!tabLost(error))
|
|
192
|
+
throw error;
|
|
193
|
+
log.warn("Dreamina tab lost, retrying on a fresh attach", { what, error: error.message });
|
|
194
|
+
return step();
|
|
195
|
+
}
|
|
196
|
+
}
|
|
128
197
|
async function callApi(path, body) {
|
|
198
|
+
return path === GENERATE_PATH ? callApiOnce(path, body) : retryOnTabLoss(path, () => callApiOnce(path, body));
|
|
199
|
+
}
|
|
200
|
+
async function callApiOnce(path, body) {
|
|
129
201
|
const query = new URLSearchParams({ aid: APP_ID, device_platform: "web", region: region(), da_version: DA_VERSION, web_version: WEB_VERSION, aigc_features: "app_lip_sync" });
|
|
130
202
|
const request = { url: `${API_BASE}${path}?${query}`, headers: signedHeaders(path), body: JSON.stringify(body) };
|
|
131
203
|
const expression = `(() => { const a = ${JSON.stringify(request)};
|
|
@@ -136,10 +208,23 @@ async function callApi(path, body) {
|
|
|
136
208
|
for (const k of Object.keys(a.headers)) x.setRequestHeader(k, a.headers[k]);
|
|
137
209
|
x.onload = () => { try { resolve(JSON.parse(x.responseText)); } catch { reject(new Error("Dreamina returned a non-JSON response (HTTP " + x.status + ")")); } };
|
|
138
210
|
x.onerror = () => reject(new Error("Dreamina request failed"));
|
|
211
|
+
x.timeout = ${API_TIMEOUT_MS};
|
|
212
|
+
x.ontimeout = () => reject(new Error("Dreamina request timed out after ${API_TIMEOUT_MS / 1000}s"));
|
|
139
213
|
x.send(a.body);
|
|
140
214
|
});
|
|
141
215
|
})()`;
|
|
142
|
-
const
|
|
216
|
+
const started = Date.now();
|
|
217
|
+
let response;
|
|
218
|
+
try {
|
|
219
|
+
response = (await evaluateBounded(expression, path));
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
// A request that ran out of time has an unknown outcome, like a lost tab.
|
|
223
|
+
if (error instanceof Error && /timed out after/.test(error.message))
|
|
224
|
+
throw new DreaminaStallError(error.message);
|
|
225
|
+
throw error;
|
|
226
|
+
}
|
|
227
|
+
log.debug("Dreamina API call", { path, ret: response.ret, ms: Date.now() - started });
|
|
143
228
|
// `submit_audit_job` answers with an empty `ret`, which carries no error.
|
|
144
229
|
if (response.ret !== "0" && response.ret !== "") {
|
|
145
230
|
const hint = response.ret === "1015" || response.ret === "1014" ? " (is the Dreamina tab logged in?)" : "";
|
|
@@ -316,7 +401,10 @@ function imagexHeaders(token, method, query, payload, service = "imagex") {
|
|
|
316
401
|
};
|
|
317
402
|
}
|
|
318
403
|
/** Upload an image through ImageX the way the web app does and return its store URI. */
|
|
319
|
-
|
|
404
|
+
function uploadImage(bytes) {
|
|
405
|
+
return retryOnTabLoss("image upload", () => uploadImageOnce(bytes));
|
|
406
|
+
}
|
|
407
|
+
async function uploadImageOnce(bytes) {
|
|
320
408
|
const token = await callApi("/mweb/v1/get_upload_token", { scene: 2 });
|
|
321
409
|
const account = (await runner().evaluate(`fetch("https://dreamina.capcut.com/passport/web/account/info/?aid=${APP_ID}&account_sdk_source=web&language=en", { credentials: "include" }).then((r) => r.json())`));
|
|
322
410
|
const userId = account.data?.user_id_str;
|
|
@@ -360,7 +448,10 @@ async function uploadImage(bytes) {
|
|
|
360
448
|
return { uri: result.ImageUri, width: result.ImageWidth, height: result.ImageHeight };
|
|
361
449
|
}
|
|
362
450
|
/** Upload a video or audio file through VOD the way the web app does and return its vid and size. VOD files audio as `video`. */
|
|
363
|
-
|
|
451
|
+
function uploadVideo(bytes) {
|
|
452
|
+
return retryOnTabLoss("video upload", () => uploadVideoOnce(bytes));
|
|
453
|
+
}
|
|
454
|
+
async function uploadVideoOnce(bytes) {
|
|
364
455
|
const token = await callApi("/mweb/v1/get_upload_token", { scene: 1 });
|
|
365
456
|
const account = (await runner().evaluate(`fetch("https://dreamina.capcut.com/passport/web/account/info/?aid=${APP_ID}&account_sdk_source=web&language=en", { credentials: "include" }).then((r) => r.json())`));
|
|
366
457
|
const userId = account.data?.user_id_str;
|
|
@@ -421,23 +512,22 @@ async function auditReferences(model, images, videos = [], audios = []) {
|
|
|
421
512
|
}
|
|
422
513
|
/**
|
|
423
514
|
* Split a prompt on `[Image N]`, `[Video N]` and `[Audio N]` markers into the
|
|
424
|
-
*
|
|
425
|
-
*
|
|
426
|
-
*
|
|
515
|
+
* segments the site's editor produces: every image, video and audio leads,
|
|
516
|
+
* then the prompt follows with each marker as an inline mention of its
|
|
517
|
+
* material. A material's index counts images first, then videos, then audios.
|
|
427
518
|
*/
|
|
428
519
|
function referenceMeta(prompt, imageCount, videoCount = 0, audioCount = 0) {
|
|
429
520
|
const counts = { image: imageCount, video: videoCount, audio: audioCount };
|
|
430
521
|
const offsets = { image: 0, video: imageCount, audio: imageCount + videoCount };
|
|
431
|
-
const media = (kind, index) => ({ meta_type: kind, text: "", material_ref: { material_idx: offsets[kind] + index } });
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
const parts = [];
|
|
522
|
+
const media = (kind, index) => ({ type: "", id: randomUUID(), meta_type: kind, text: "", material_ref: { type: "", id: randomUUID(), material_idx: offsets[kind] + index } });
|
|
523
|
+
const text = (value) => ({ type: "", id: randomUUID(), meta_type: "text", text: value });
|
|
524
|
+
// Captured from the web app: the materials always lead, in order, even when
|
|
525
|
+
// the prompt mentions them again inline.
|
|
526
|
+
const parts = [
|
|
527
|
+
...Array.from({ length: imageCount }, (_, i) => media("image", i)),
|
|
528
|
+
...Array.from({ length: videoCount }, (_, i) => media("video", i)),
|
|
529
|
+
...Array.from({ length: audioCount }, (_, i) => media("audio", i))
|
|
530
|
+
];
|
|
441
531
|
let last = 0;
|
|
442
532
|
for (const match of prompt.matchAll(/\[(Image|Video|Audio) (\d+)\]/g)) {
|
|
443
533
|
const kind = match[1].toLowerCase();
|
|
@@ -445,22 +535,27 @@ function referenceMeta(prompt, imageCount, videoCount = 0, audioCount = 0) {
|
|
|
445
535
|
if (index < 0 || index >= counts[kind])
|
|
446
536
|
throw new Error(`The prompt refers to [${match[1]} ${match[2]}] but only ${counts[kind]} reference ${kind}(s) were given`);
|
|
447
537
|
if (match.index > last)
|
|
448
|
-
parts.push(
|
|
538
|
+
parts.push(text(prompt.slice(last, match.index)));
|
|
449
539
|
parts.push(media(kind, index));
|
|
450
540
|
last = match.index + match[0].length;
|
|
451
541
|
}
|
|
452
542
|
if (last < prompt.length)
|
|
453
|
-
parts.push(
|
|
543
|
+
parts.push(text(prompt.slice(last)));
|
|
454
544
|
return parts;
|
|
455
545
|
}
|
|
456
546
|
function videoDraftFor(model, prompt, seed, ratio, resolution, durationMs, submitId, references = [], frames, videos = [], audios = []) {
|
|
457
547
|
const id = () => randomUUID();
|
|
548
|
+
// Seedance 2.5's 480p preview model renders in Dreamina's draft mode. The web
|
|
549
|
+
// app marks the input as a draft on the current schema version, and the API
|
|
550
|
+
// answers "invalid parameter" (ret 1000) without it.
|
|
551
|
+
const draftMode = model.endsWith("_draft");
|
|
458
552
|
const frameImage = (image) => ({ type: "image", source_from: "upload", platform_type: 1, name: "", image_uri: image.uri, aigc_image: {}, width: image.width, height: image.height, format: "", uri: image.uri });
|
|
459
553
|
const videoInput = {
|
|
460
554
|
type: "",
|
|
461
555
|
id: id(),
|
|
462
|
-
min_version: references.length + videos.length + audios.length > 0 ? "3.3.9" : "3.0.5",
|
|
556
|
+
min_version: draftMode ? DA_VERSION : references.length + videos.length + audios.length > 0 ? "3.3.9" : "3.0.5",
|
|
463
557
|
prompt: references.length + videos.length + audios.length > 0 ? "" : prompt,
|
|
558
|
+
...(draftMode && { is_draft_mode: true }),
|
|
464
559
|
video_mode: 2,
|
|
465
560
|
fps: VIDEO_FPS,
|
|
466
561
|
duration_ms: durationMs,
|
|
@@ -477,12 +572,18 @@ function videoDraftFor(model, prompt, seed, ratio, resolution, durationMs, submi
|
|
|
477
572
|
if (references.length + videos.length + audios.length > 0) {
|
|
478
573
|
videoInput.unified_edit_input = {
|
|
479
574
|
material_list: [...references.map((image) => ({
|
|
575
|
+
type: "",
|
|
576
|
+
id: id(),
|
|
480
577
|
material_type: "image",
|
|
481
578
|
image_info: { type: "image", source_from: "upload", platform_type: 1, name: "", image_uri: image.uri, aigc_image: {}, width: image.width, height: image.height, format: "", title: "", uri: image.uri }
|
|
482
579
|
})), ...videos.map((video) => ({
|
|
580
|
+
type: "",
|
|
581
|
+
id: id(),
|
|
483
582
|
material_type: "video",
|
|
484
583
|
video_info: { type: "video", source_from: "upload", name: "", vid: video.vid, fps: 0, width: video.width, height: video.height, duration: video.durationMs }
|
|
485
584
|
})), ...audios.map((audio) => ({
|
|
585
|
+
type: "",
|
|
586
|
+
id: id(),
|
|
486
587
|
material_type: "audio",
|
|
487
588
|
audio_info: { type: "audio", source_from: "upload", vid: audio.vid, duration: audio.durationMs, name: "" }
|
|
488
589
|
}))],
|
|
@@ -516,13 +617,57 @@ function videoDraftFor(model, prompt, seed, ratio, resolution, durationMs, submi
|
|
|
516
617
|
},
|
|
517
618
|
process_type: 1
|
|
518
619
|
};
|
|
519
|
-
|
|
620
|
+
// Captured from the web app: reference input declares the unified-edit
|
|
621
|
+
// feature on the 3.3.9 schema, and every Seedance 2.5 model the 2.5 result
|
|
622
|
+
// action.
|
|
623
|
+
const unifiedEdit = references.length + videos.length + audios.length > 0;
|
|
624
|
+
const minFeatures = [
|
|
625
|
+
...(unifiedEdit ? ["AIGC_Video_UnifiedEdit"] : []),
|
|
626
|
+
...(model.includes("seedance_45") ? ["AIGC_Video_Seedance25ResultAction"] : [])
|
|
627
|
+
];
|
|
628
|
+
const minVersion = draftMode ? DA_VERSION : unifiedEdit ? "3.3.9" : "3.0.5";
|
|
629
|
+
return JSON.stringify({ type: "draft", id: id(), min_version: minVersion, min_features: minFeatures, is_from_tsn: true, version: DA_VERSION, main_component_id: component.id, component_list: [component] });
|
|
520
630
|
}
|
|
521
631
|
/** Submit a generation and poll until it finishes, then return the finished record. */
|
|
522
632
|
async function submitAndAwait(body, submitId, timeoutMs, signal) {
|
|
523
|
-
|
|
524
|
-
|
|
633
|
+
// A lost tab leaves the submit's outcome unknown: Dreamina may already run
|
|
634
|
+
// (and bill) the job. Sending it again could pay twice, so the job is looked
|
|
635
|
+
// up by its submit id instead, and only a job that never appears fails.
|
|
636
|
+
let unconfirmed = null;
|
|
637
|
+
try {
|
|
638
|
+
await callApi(GENERATE_PATH, body);
|
|
639
|
+
}
|
|
640
|
+
catch (error) {
|
|
641
|
+
if (!tabLost(error) && !stalled(error)) {
|
|
642
|
+
throw new DreaminaSubmitRejectedError(error instanceof Error ? error.message : String(error), { cause: error });
|
|
643
|
+
}
|
|
644
|
+
unconfirmed = error;
|
|
645
|
+
log.warn("Dreamina submit has no answer, looking the job up by id", { submitId, error: unconfirmed.message });
|
|
646
|
+
}
|
|
647
|
+
log.info("Dreamina generation submitted", { submitId, timeoutMs, confirmed: !unconfirmed });
|
|
648
|
+
return awaitJob(submitId, timeoutMs, signal, unconfirmed);
|
|
649
|
+
}
|
|
650
|
+
/** Dreamina answered the submit with an error, so no job runs. */
|
|
651
|
+
class DreaminaSubmitRejectedError extends Error {
|
|
652
|
+
}
|
|
653
|
+
/** The job is not in Dreamina's history: it never arrived, or the history dropped it. */
|
|
654
|
+
class DreaminaJobMissingError extends Error {
|
|
655
|
+
}
|
|
656
|
+
/** A job that Dreamina itself reported as failed. Asking again for the same job cannot help. */
|
|
657
|
+
class DreaminaJobFailedError extends Error {
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Poll the job `submitId` until it finishes and return its record. A job that
|
|
661
|
+
* does not show in the history within {@link UNCONFIRMED_SUBMIT_GRACE_MS} is
|
|
662
|
+
* missing, when its submit was not confirmed (`unconfirmed`) or when it is an
|
|
663
|
+
* older job picked up again (`unconfirmed` is null and `resumed` is true).
|
|
664
|
+
*/
|
|
665
|
+
async function awaitJob(submitId, timeoutMs, signal, unconfirmed = null, resumed = false) {
|
|
666
|
+
const started = Date.now();
|
|
667
|
+
const deadline = started + timeoutMs;
|
|
525
668
|
let pollFailures = 0;
|
|
669
|
+
let lastStatus;
|
|
670
|
+
let seen = false;
|
|
526
671
|
for (;;) {
|
|
527
672
|
await sleep(POLL_INTERVAL_MS, signal);
|
|
528
673
|
let history;
|
|
@@ -532,28 +677,86 @@ async function submitAndAwait(body, submitId, timeoutMs, signal) {
|
|
|
532
677
|
catch (error) {
|
|
533
678
|
// Reading the history is idempotent, and Dreamina answers it with a
|
|
534
679
|
// transient error while a fresh task registers. The submit is never retried.
|
|
680
|
+
log.warn("Dreamina poll failed", { submitId, pollFailures: pollFailures + 1, error: error instanceof Error ? error.message : String(error) });
|
|
535
681
|
if (++pollFailures > MAX_POLL_FAILURES)
|
|
536
682
|
throw error;
|
|
683
|
+
if (Date.now() > deadline) {
|
|
684
|
+
throw new Error(`Dreamina generation ${submitId} timed out after ${timeoutMs / 1000}s (last task status ${lastStatus ?? "none"}, last poll failed: ${error instanceof Error ? error.message : String(error)})`);
|
|
685
|
+
}
|
|
537
686
|
continue;
|
|
538
687
|
}
|
|
539
688
|
pollFailures = 0;
|
|
540
689
|
const record = history[submitId];
|
|
690
|
+
if (record) {
|
|
691
|
+
seen = true;
|
|
692
|
+
}
|
|
693
|
+
else if (!seen && (unconfirmed || resumed) && Date.now() - started > UNCONFIRMED_SUBMIT_GRACE_MS) {
|
|
694
|
+
throw new DreaminaJobMissingError(unconfirmed
|
|
695
|
+
? `Dreamina generation ${submitId} did not reach Dreamina before the tab was lost (${unconfirmed.message}). Keep the Dreamina tab open and try again`
|
|
696
|
+
: `Dreamina generation ${submitId} is no longer in the Dreamina history`);
|
|
697
|
+
}
|
|
541
698
|
const status = record?.task?.status;
|
|
699
|
+
if (status !== lastStatus) {
|
|
700
|
+
log.info("Dreamina task status", { submitId, status, hasRecord: record !== undefined, elapsedMs: Date.now() - started });
|
|
701
|
+
lastStatus = status;
|
|
702
|
+
}
|
|
542
703
|
if (status === TASK_DONE)
|
|
543
704
|
return record;
|
|
544
705
|
if (status === TASK_FAILED) {
|
|
545
|
-
throw new
|
|
706
|
+
throw new DreaminaJobFailedError(`Dreamina generation failed: ${record?.fail_msg || "unknown reason"} (fail_code ${record?.fail_code ?? "unknown"})`);
|
|
707
|
+
}
|
|
708
|
+
if (Date.now() > deadline) {
|
|
709
|
+
log.warn("Dreamina generation timed out", { submitId, lastStatus, record: JSON.stringify(record ?? null).slice(0, 2000) });
|
|
710
|
+
throw new Error(`Dreamina generation ${submitId} timed out after ${timeoutMs / 1000}s (last task status ${lastStatus ?? "none"}). The job keeps running on Dreamina; the same request picks it up again instead of paying for a new one`);
|
|
546
711
|
}
|
|
547
|
-
if (Date.now() > deadline)
|
|
548
|
-
throw new Error(`Dreamina generation timed out after ${timeoutMs / 1000}s`);
|
|
549
712
|
}
|
|
550
713
|
}
|
|
551
|
-
async function
|
|
552
|
-
const
|
|
714
|
+
async function downloadOnce(url, signal) {
|
|
715
|
+
const limit = AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS);
|
|
716
|
+
const combined = signal ? AbortSignal.any([signal, limit]) : limit;
|
|
717
|
+
const response = await fetchExternalMedia(url, { signal: combined });
|
|
553
718
|
if (!response.ok)
|
|
554
719
|
throw new Error(`Dreamina download failed: HTTP ${response.status}`);
|
|
555
720
|
return new Uint8Array(await response.arrayBuffer());
|
|
556
721
|
}
|
|
722
|
+
/** Fetch `url` inside the Dreamina tab, which can always reach Dreamina's CDN. */
|
|
723
|
+
async function downloadViaTab(url) {
|
|
724
|
+
const expression = `(async () => {
|
|
725
|
+
const response = await fetch(${JSON.stringify(url)}, { credentials: "omit", signal: AbortSignal.timeout(${DOWNLOAD_TIMEOUT_MS}) });
|
|
726
|
+
if (!response.ok) throw new Error("Dreamina download failed in the tab: HTTP " + response.status);
|
|
727
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
728
|
+
let binary = "";
|
|
729
|
+
for (let i = 0; i < bytes.length; i += 0x8000) binary += String.fromCharCode.apply(null, bytes.subarray(i, i + 0x8000));
|
|
730
|
+
return btoa(binary);
|
|
731
|
+
})()`;
|
|
732
|
+
const base64 = (await evaluateBounded(expression, "the download", DOWNLOAD_TIMEOUT_MS + 15_000));
|
|
733
|
+
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Download a finished result. The job is paid for by now, so a slow or broken
|
|
737
|
+
* connection gets a second attempt, then the tab's own connection.
|
|
738
|
+
*/
|
|
739
|
+
async function download(url, signal) {
|
|
740
|
+
let lastError;
|
|
741
|
+
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
742
|
+
try {
|
|
743
|
+
return await downloadOnce(url, signal);
|
|
744
|
+
}
|
|
745
|
+
catch (error) {
|
|
746
|
+
if (signal?.aborted)
|
|
747
|
+
throw error;
|
|
748
|
+
lastError = error;
|
|
749
|
+
log.warn("Dreamina download failed", { attempt, error: error instanceof Error ? error.message : String(error) });
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
try {
|
|
753
|
+
return await downloadViaTab(url);
|
|
754
|
+
}
|
|
755
|
+
catch (error) {
|
|
756
|
+
log.warn("Dreamina download through the tab failed", { error: error instanceof Error ? error.message : String(error) });
|
|
757
|
+
throw new Error(`Dreamina download failed: ${lastError instanceof Error ? lastError.message : String(lastError)}. The result is still in your Dreamina history`);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
557
760
|
function videoEnum(model, key) {
|
|
558
761
|
const option = model.options?.find((o) => o.key === key)?.enum_val;
|
|
559
762
|
const values = (option?.string_value ?? option?.int_value ?? []);
|
|
@@ -563,6 +766,34 @@ function videoEnum(model, key) {
|
|
|
563
766
|
function takesReferences(model) {
|
|
564
767
|
return Boolean(model.options?.some((o) => o.key === "unified_edit")) || videoEnum(model, "input_media_type").values.includes("unified_edit");
|
|
565
768
|
}
|
|
769
|
+
/**
|
|
770
|
+
* Turn a start frame, an end frame and named references into omni-reference
|
|
771
|
+
* input: every image becomes a reference, and the prompt opens with one
|
|
772
|
+
* mention per image (`[Image 2] is Mara.`), which the draft turns into inline
|
|
773
|
+
* image mentions. The caller's own references keep their numbers, so markers
|
|
774
|
+
* already in the prompt still point at them. Named references past the
|
|
775
|
+
* model's limit drop.
|
|
776
|
+
*/
|
|
777
|
+
function omniInputs(prompt, inputs) {
|
|
778
|
+
const references = [...inputs.references];
|
|
779
|
+
const mentions = [];
|
|
780
|
+
const mention = (image, says) => {
|
|
781
|
+
references.push(image);
|
|
782
|
+
mentions.push(says(`[Image ${references.length}]`));
|
|
783
|
+
};
|
|
784
|
+
if (inputs.first)
|
|
785
|
+
mention(inputs.first, (m) => `${m} is the first frame.`);
|
|
786
|
+
if (inputs.last)
|
|
787
|
+
mention(inputs.last, (m) => `${m} is the last frame.`);
|
|
788
|
+
for (const named of inputs.named ?? []) {
|
|
789
|
+
if (references.length >= MAX_REFERENCE_IMAGES) {
|
|
790
|
+
log.warn("Dreamina reference limit reached, dropping a named reference", { name: named.name, limit: MAX_REFERENCE_IMAGES });
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
mention(named.image, (m) => (named.name ? `${m} is ${named.name}.` : `${m} is a reference.`));
|
|
794
|
+
}
|
|
795
|
+
return { prompt: [mentions.join(" "), prompt].filter((part) => part.trim().length > 0).join("\n\n"), references };
|
|
796
|
+
}
|
|
566
797
|
function videoInputTypes(model) {
|
|
567
798
|
return videoEnum(model, "input_media_type").values.map(String);
|
|
568
799
|
}
|
|
@@ -688,15 +919,17 @@ export class DreaminaProvider extends BaseProvider {
|
|
|
688
919
|
return [...DREAMINA_VIDEO_MODELS];
|
|
689
920
|
}
|
|
690
921
|
async textToVideo(params) {
|
|
691
|
-
return this.generateVideo(params, { references: [] });
|
|
922
|
+
return this.generateVideo(params, { references: [], named: params.references ?? [] });
|
|
692
923
|
}
|
|
693
924
|
/**
|
|
694
|
-
* Image-to-video
|
|
695
|
-
*
|
|
696
|
-
*
|
|
925
|
+
* Image-to-video. Models that take references (Seedance 2.x) always render
|
|
926
|
+
* in "omni reference" mode: the image, `endImage` and `references` all go in
|
|
927
|
+
* as references, and the prompt names each one, starting with
|
|
928
|
+
* `[Image 1] is the first frame.` Older models use "first and last frames"
|
|
929
|
+
* mode, where `endImage` needs `end_frame` and references are ignored.
|
|
697
930
|
*/
|
|
698
931
|
async imageToVideo(image, params) {
|
|
699
|
-
return this.generateVideo({ ...params, prompt: params.prompt ?? "" }, { references: [], first: image, last: params.endImage ?? undefined });
|
|
932
|
+
return this.generateVideo({ ...params, prompt: params.prompt ?? "" }, { references: [], first: image, last: params.endImage ?? undefined, named: params.references ?? [] });
|
|
700
933
|
}
|
|
701
934
|
/**
|
|
702
935
|
* Reference-to-video through Dreamina's "omni reference" mode: the images
|
|
@@ -716,7 +949,7 @@ export class DreaminaProvider extends BaseProvider {
|
|
|
716
949
|
throw new Error(`Dreamina takes at most ${MAX_REFERENCE_IMAGES} reference images, not ${inputs.images.length}`);
|
|
717
950
|
if (inputs.videos.length > MAX_REFERENCE_VIDEOS)
|
|
718
951
|
throw new Error(`Dreamina takes at most ${MAX_REFERENCE_VIDEOS} reference videos, not ${inputs.videos.length}`);
|
|
719
|
-
return this.generateVideo(params, { references: inputs.images, videos: inputs.videos, audios });
|
|
952
|
+
return this.generateVideo(params, { references: inputs.images, videos: inputs.videos, audios, named: params.references ?? undefined });
|
|
720
953
|
}
|
|
721
954
|
async getAvailableMusicModels() {
|
|
722
955
|
return this.music.models();
|
|
@@ -725,10 +958,20 @@ export class DreaminaProvider extends BaseProvider {
|
|
|
725
958
|
async textToMusic(params) {
|
|
726
959
|
return this.music.generate(params);
|
|
727
960
|
}
|
|
728
|
-
async generateVideo(params,
|
|
961
|
+
async generateVideo(params, requested) {
|
|
729
962
|
const config = (await this.videoConfigs()).find((m) => m.model_req_key === params.model.id);
|
|
730
963
|
if (!config)
|
|
731
964
|
throw new Error(`Unknown Dreamina video model: ${params.model.id}`);
|
|
965
|
+
// Models that take references always render in omni-reference mode, so a
|
|
966
|
+
// start frame sits beside the entity images instead of excluding them.
|
|
967
|
+
const omni = takesReferences(config) && Boolean(requested.first || requested.named?.length)
|
|
968
|
+
? omniInputs(params.prompt, requested)
|
|
969
|
+
: null;
|
|
970
|
+
if (!omni && requested.named?.length) {
|
|
971
|
+
log.debug("Dreamina model takes no references, ignoring them", { model: config.model_req_key, count: requested.named.length });
|
|
972
|
+
}
|
|
973
|
+
const inputs = omni ? { ...requested, references: omni.references, first: undefined, last: undefined } : requested;
|
|
974
|
+
const prompt = omni?.prompt ?? params.prompt;
|
|
732
975
|
const resolutions = videoEnum(config, "resolution");
|
|
733
976
|
const resolution = params.resolution
|
|
734
977
|
? resolutions.values.map(String).find((r) => r.toLowerCase() === params.resolution.toLowerCase())
|
|
@@ -753,6 +996,22 @@ export class DreaminaProvider extends BaseProvider {
|
|
|
753
996
|
throw new Error(`Dreamina model ${config.model_name} does not take a first frame image`);
|
|
754
997
|
if (inputs.last && !videoInputTypes(config).includes("end_frame"))
|
|
755
998
|
throw new Error(`Dreamina model ${config.model_name} does not take a last frame image`);
|
|
999
|
+
const timeoutMs = (params.timeoutSeconds ?? VIDEO_TIMEOUT_MS / 1000) * 1000;
|
|
1000
|
+
// A random seed is not part of the request's identity: a retry draws a new one.
|
|
1001
|
+
const key = pendingJobKey({ kind: "video", model: config.model_req_key, prompt, ratio, resolution, seconds, seed: params.seed ?? null, counts: [inputs.references.length, videoInputs.length, audioInputs.length] }, [...inputs.references, ...videoInputs, ...audioInputs, inputs.first, inputs.last]);
|
|
1002
|
+
const earlier = pendingJobs().find(key);
|
|
1003
|
+
if (earlier) {
|
|
1004
|
+
log.info("Dreamina picks up the earlier job for this request", { submitId: earlier });
|
|
1005
|
+
try {
|
|
1006
|
+
return await this.collectVideo(config.model_req_key, key, await awaitJob(earlier, timeoutMs, params.signal, null, true), earlier, params.signal);
|
|
1007
|
+
}
|
|
1008
|
+
catch (error) {
|
|
1009
|
+
if (!(error instanceof DreaminaJobMissingError || error instanceof DreaminaJobFailedError))
|
|
1010
|
+
throw error;
|
|
1011
|
+
pendingJobs().remove(key);
|
|
1012
|
+
log.warn("The earlier Dreamina job cannot be collected, submitting a new one", { submitId: earlier, error: error.message });
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
756
1015
|
const references = [];
|
|
757
1016
|
for (const image of inputs.references)
|
|
758
1017
|
references.push(await uploadImage(image));
|
|
@@ -773,18 +1032,39 @@ export class DreaminaProvider extends BaseProvider {
|
|
|
773
1032
|
await auditReferences(config.model_req_key, audited, videos, audios);
|
|
774
1033
|
const submitId = randomUUID();
|
|
775
1034
|
const seed = params.seed ?? Math.floor(Math.random() * 4_000_000_000);
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
1035
|
+
// Recorded before the submit: a submit whose answer is lost may still run.
|
|
1036
|
+
pendingJobs().add(key, submitId);
|
|
1037
|
+
let record;
|
|
1038
|
+
try {
|
|
1039
|
+
record = await submitAndAwait({
|
|
1040
|
+
extend: { root_model: config.model_req_key },
|
|
1041
|
+
submit_id: submitId,
|
|
1042
|
+
metrics_extra: JSON.stringify({ isDefaultSeed: 1, originSubmitId: submitId, isRegenerate: false }),
|
|
1043
|
+
draft_content: videoDraftFor(config.model_req_key, prompt, seed, ratio, resolution, seconds * 1000, submitId, references, bounds, videos, audios),
|
|
1044
|
+
http_common_info: { aid: Number(APP_ID) }
|
|
1045
|
+
}, submitId, timeoutMs, params.signal);
|
|
1046
|
+
}
|
|
1047
|
+
catch (error) {
|
|
1048
|
+
// A rejected submit or a failed job leaves nothing to collect. Any other
|
|
1049
|
+
// error (a timeout, a lost tab, a cancel) may leave a running, paid job.
|
|
1050
|
+
if (!(error instanceof DreaminaSubmitRejectedError || error instanceof DreaminaJobFailedError || error instanceof DreaminaJobMissingError))
|
|
1051
|
+
throw error;
|
|
1052
|
+
pendingJobs().remove(key);
|
|
1053
|
+
throw error;
|
|
1054
|
+
}
|
|
1055
|
+
return this.collectVideo(config.model_req_key, key, record, submitId, params.signal);
|
|
1056
|
+
}
|
|
1057
|
+
/** Download a finished job's video and drop its record. */
|
|
1058
|
+
async collectVideo(model, key, record, submitId, signal) {
|
|
783
1059
|
const url = record.item_list?.[0]?.video?.transcoded_video?.origin?.video_url;
|
|
784
|
-
if (!url)
|
|
1060
|
+
if (!url) {
|
|
1061
|
+
pendingJobs().remove(key);
|
|
785
1062
|
throw new Error("Dreamina finished without a video");
|
|
786
|
-
|
|
787
|
-
|
|
1063
|
+
}
|
|
1064
|
+
const video = await download(url, signal ?? undefined);
|
|
1065
|
+
pendingJobs().remove(key);
|
|
1066
|
+
log.debug("Dreamina video generation completed", { model, submitId });
|
|
1067
|
+
return video;
|
|
788
1068
|
}
|
|
789
1069
|
}
|
|
790
1070
|
//# sourceMappingURL=dreamina-provider.js.map
|