varg.ai-sdk 0.1.0 → 0.4.0-alpha.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/.claude/settings.local.json +1 -1
- package/.env.example +3 -0
- package/.github/workflows/ci.yml +23 -0
- package/.husky/README.md +102 -0
- package/.husky/commit-msg +6 -0
- package/.husky/pre-commit +9 -0
- package/.husky/pre-push +6 -0
- package/.size-limit.json +8 -0
- package/.test-hooks.ts +5 -0
- package/CLAUDE.md +10 -3
- package/CONTRIBUTING.md +150 -0
- package/LICENSE.md +53 -0
- package/README.md +56 -209
- package/SKILLS.md +26 -10
- package/biome.json +7 -1
- package/bun.lock +1286 -0
- package/commitlint.config.js +22 -0
- package/docs/index.html +1130 -0
- package/docs/prompting.md +326 -0
- package/docs/react.md +834 -0
- package/docs/sdk.md +812 -0
- package/ffmpeg/CLAUDE.md +68 -0
- package/package.json +48 -8
- package/pipeline/cookbooks/scripts/animate-frames-parallel.ts +84 -0
- package/pipeline/cookbooks/scripts/combine-scenes.sh +53 -0
- package/pipeline/cookbooks/scripts/generate-frames-parallel.ts +99 -0
- package/pipeline/cookbooks/scripts/still-to-video.sh +37 -0
- package/pipeline/cookbooks/text-to-tiktok.md +669 -0
- package/pipeline/cookbooks/trendwatching.md +156 -0
- package/plan.md +281 -0
- package/scripts/.gitkeep +0 -0
- package/src/ai-sdk/cache.ts +142 -0
- package/src/ai-sdk/examples/cached-generation.ts +53 -0
- package/src/ai-sdk/examples/duet-scene-4.ts +53 -0
- package/src/ai-sdk/examples/duet-scene-5-audio.ts +32 -0
- package/src/ai-sdk/examples/duet-video.ts +56 -0
- package/src/ai-sdk/examples/editly-composition.ts +63 -0
- package/src/ai-sdk/examples/editly-test.ts +57 -0
- package/src/ai-sdk/examples/editly-video-test.ts +52 -0
- package/src/ai-sdk/examples/fal-lipsync.ts +43 -0
- package/src/ai-sdk/examples/higgsfield-image.ts +61 -0
- package/src/ai-sdk/examples/music-generation.ts +19 -0
- package/src/ai-sdk/examples/openai-sora.ts +34 -0
- package/src/ai-sdk/examples/replicate-bg-removal.ts +52 -0
- package/src/ai-sdk/examples/simpsons-scene.ts +61 -0
- package/src/ai-sdk/examples/talking-lion.ts +55 -0
- package/src/ai-sdk/examples/video-generation.ts +39 -0
- package/src/ai-sdk/examples/workflow-animated-girl.ts +104 -0
- package/src/ai-sdk/examples/workflow-before-after.ts +114 -0
- package/src/ai-sdk/examples/workflow-character-grid.ts +112 -0
- package/src/ai-sdk/examples/workflow-slideshow.ts +161 -0
- package/src/ai-sdk/file-cache.ts +112 -0
- package/src/ai-sdk/file.ts +238 -0
- package/src/ai-sdk/generate-element.ts +92 -0
- package/src/ai-sdk/generate-music.ts +46 -0
- package/src/ai-sdk/generate-video.ts +165 -0
- package/src/ai-sdk/index.ts +72 -0
- package/src/ai-sdk/music-model.ts +110 -0
- package/src/ai-sdk/providers/editly/editly.test.ts +1108 -0
- package/src/ai-sdk/providers/editly/ffmpeg.ts +60 -0
- package/src/ai-sdk/providers/editly/index.ts +817 -0
- package/src/ai-sdk/providers/editly/layers.ts +776 -0
- package/src/ai-sdk/providers/editly/plan.md +144 -0
- package/src/ai-sdk/providers/editly/types.ts +328 -0
- package/src/ai-sdk/providers/elevenlabs-provider.ts +255 -0
- package/src/ai-sdk/providers/fal-provider.ts +512 -0
- package/src/ai-sdk/providers/higgsfield.ts +379 -0
- package/src/ai-sdk/providers/openai.ts +251 -0
- package/src/ai-sdk/providers/replicate.ts +16 -0
- package/src/ai-sdk/video-model.ts +185 -0
- package/src/cli/commands/find.tsx +137 -0
- package/src/cli/commands/help.tsx +85 -0
- package/src/cli/commands/index.ts +6 -0
- package/src/cli/commands/list.tsx +238 -0
- package/src/cli/commands/render.tsx +71 -0
- package/src/cli/commands/run.tsx +511 -0
- package/src/cli/commands/which.tsx +253 -0
- package/src/cli/index.ts +114 -0
- package/src/cli/quiet.ts +44 -0
- package/src/cli/types.ts +32 -0
- package/src/cli/ui/components/Badge.tsx +29 -0
- package/src/cli/ui/components/DataTable.tsx +51 -0
- package/src/cli/ui/components/Header.tsx +23 -0
- package/src/cli/ui/components/HelpBlock.tsx +44 -0
- package/src/cli/ui/components/KeyValue.tsx +33 -0
- package/src/cli/ui/components/OptionRow.tsx +81 -0
- package/src/cli/ui/components/Separator.tsx +23 -0
- package/src/cli/ui/components/StatusBox.tsx +108 -0
- package/src/cli/ui/components/VargBox.tsx +51 -0
- package/src/cli/ui/components/VargProgress.tsx +36 -0
- package/src/cli/ui/components/VargSpinner.tsx +34 -0
- package/src/cli/ui/components/VargText.tsx +56 -0
- package/src/cli/ui/components/index.ts +19 -0
- package/src/cli/ui/index.ts +12 -0
- package/src/cli/ui/render.ts +35 -0
- package/src/cli/ui/theme.ts +63 -0
- package/src/cli/utils.ts +78 -0
- package/src/core/executor/executor.ts +201 -0
- package/src/core/executor/index.ts +13 -0
- package/src/core/executor/job.ts +214 -0
- package/src/core/executor/pipeline.ts +222 -0
- package/src/core/index.ts +11 -0
- package/src/core/registry/index.ts +9 -0
- package/src/core/registry/loader.ts +149 -0
- package/src/core/registry/registry.ts +221 -0
- package/src/core/registry/resolver.ts +206 -0
- package/src/core/schema/helpers.ts +134 -0
- package/src/core/schema/index.ts +8 -0
- package/src/core/schema/shared.ts +102 -0
- package/src/core/schema/types.ts +279 -0
- package/src/core/schema/validator.ts +92 -0
- package/src/definitions/actions/captions.ts +261 -0
- package/src/definitions/actions/edit.ts +298 -0
- package/src/definitions/actions/image.ts +125 -0
- package/src/definitions/actions/index.ts +114 -0
- package/src/definitions/actions/music.ts +205 -0
- package/src/definitions/actions/sync.ts +128 -0
- package/{action/transcribe/index.ts → src/definitions/actions/transcribe.ts} +63 -90
- package/src/definitions/actions/upload.ts +111 -0
- package/src/definitions/actions/video.ts +163 -0
- package/src/definitions/actions/voice.ts +119 -0
- package/src/definitions/index.ts +23 -0
- package/src/definitions/models/elevenlabs.ts +50 -0
- package/src/definitions/models/flux.ts +56 -0
- package/src/definitions/models/index.ts +36 -0
- package/src/definitions/models/kling.ts +56 -0
- package/src/definitions/models/llama.ts +54 -0
- package/src/definitions/models/nano-banana-pro.ts +102 -0
- package/src/definitions/models/sonauto.ts +68 -0
- package/src/definitions/models/soul.ts +65 -0
- package/src/definitions/models/wan.ts +54 -0
- package/src/definitions/models/whisper.ts +44 -0
- package/src/definitions/skills/index.ts +12 -0
- package/src/definitions/skills/talking-character.ts +87 -0
- package/src/definitions/skills/text-to-tiktok.ts +97 -0
- package/src/index.ts +118 -0
- package/src/providers/apify.ts +269 -0
- package/src/providers/base.ts +264 -0
- package/src/providers/elevenlabs.ts +217 -0
- package/src/providers/fal.ts +392 -0
- package/src/providers/ffmpeg.ts +544 -0
- package/src/providers/fireworks.ts +193 -0
- package/src/providers/groq.ts +149 -0
- package/src/providers/higgsfield.ts +145 -0
- package/src/providers/index.ts +143 -0
- package/src/providers/replicate.ts +147 -0
- package/src/providers/storage.ts +206 -0
- package/src/react/cli.ts +52 -0
- package/src/react/elements.ts +146 -0
- package/src/react/examples/branching.tsx +66 -0
- package/src/react/examples/captions-demo.tsx +37 -0
- package/src/react/examples/character-video.tsx +84 -0
- package/src/react/examples/grid.tsx +53 -0
- package/src/react/examples/layouts-demo.tsx +57 -0
- package/src/react/examples/madi.tsx +60 -0
- package/src/react/examples/music-test.tsx +35 -0
- package/src/react/examples/onlyfans-1m/workflow.tsx +88 -0
- package/src/react/examples/orange-portrait.tsx +41 -0
- package/src/react/examples/split-element-demo.tsx +60 -0
- package/src/react/examples/split-layout-demo.tsx +60 -0
- package/src/react/examples/split.tsx +41 -0
- package/src/react/examples/video-grid.tsx +46 -0
- package/src/react/index.ts +43 -0
- package/src/react/layouts/grid.tsx +28 -0
- package/src/react/layouts/index.ts +2 -0
- package/src/react/layouts/split.tsx +20 -0
- package/src/react/react.test.ts +309 -0
- package/src/react/render.ts +21 -0
- package/src/react/renderers/animate.ts +59 -0
- package/src/react/renderers/captions.ts +297 -0
- package/src/react/renderers/clip.ts +248 -0
- package/src/react/renderers/context.ts +17 -0
- package/src/react/renderers/image.ts +109 -0
- package/src/react/renderers/index.ts +22 -0
- package/src/react/renderers/music.ts +60 -0
- package/src/react/renderers/packshot.ts +84 -0
- package/src/react/renderers/progress.ts +173 -0
- package/src/react/renderers/render.ts +243 -0
- package/src/react/renderers/slider.ts +69 -0
- package/src/react/renderers/speech.ts +53 -0
- package/src/react/renderers/split.ts +91 -0
- package/src/react/renderers/subtitle.ts +16 -0
- package/src/react/renderers/swipe.ts +75 -0
- package/src/react/renderers/title.ts +17 -0
- package/src/react/renderers/utils.ts +124 -0
- package/src/react/renderers/video.ts +127 -0
- package/src/react/runtime/jsx-dev-runtime.ts +43 -0
- package/src/react/runtime/jsx-runtime.ts +35 -0
- package/src/react/types.ts +232 -0
- package/src/studio/index.ts +26 -0
- package/src/studio/scanner.ts +102 -0
- package/src/studio/server.ts +554 -0
- package/src/studio/stages.ts +251 -0
- package/src/studio/step-renderer.ts +279 -0
- package/src/studio/types.ts +60 -0
- package/src/studio/ui/cache.html +303 -0
- package/src/studio/ui/index.html +1820 -0
- package/src/tests/all.test.ts +509 -0
- package/src/tests/index.ts +33 -0
- package/src/tests/unit.test.ts +403 -0
- package/tsconfig.cli.json +8 -0
- package/tsconfig.json +21 -3
- package/TEST_RESULTS.md +0 -122
- package/action/captions/SKILL.md +0 -170
- package/action/captions/index.ts +0 -227
- package/action/edit/SKILL.md +0 -235
- package/action/edit/index.ts +0 -493
- package/action/image/SKILL.md +0 -140
- package/action/image/index.ts +0 -112
- package/action/sync/SKILL.md +0 -136
- package/action/sync/index.ts +0 -187
- package/action/transcribe/SKILL.md +0 -179
- package/action/video/SKILL.md +0 -116
- package/action/video/index.ts +0 -135
- package/action/voice/SKILL.md +0 -125
- package/action/voice/index.ts +0 -201
- package/index.ts +0 -38
- package/lib/README.md +0 -144
- package/lib/ai-sdk/fal.ts +0 -106
- package/lib/ai-sdk/replicate.ts +0 -107
- package/lib/elevenlabs.ts +0 -382
- package/lib/fal.ts +0 -478
- package/lib/ffmpeg.ts +0 -467
- package/lib/fireworks.ts +0 -235
- package/lib/groq.ts +0 -246
- package/lib/higgsfield.ts +0 -176
- package/lib/remotion/SKILL.md +0 -823
- package/lib/remotion/cli.ts +0 -115
- package/lib/remotion/functions.ts +0 -283
- package/lib/remotion/index.ts +0 -19
- package/lib/remotion/templates.ts +0 -73
- package/lib/replicate.ts +0 -304
- package/output.txt +0 -1
- package/test-import.ts +0 -7
- package/test-services.ts +0 -97
- package/utilities/s3.ts +0 -147
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main Execution Orchestrator
|
|
3
|
+
* Routes execution to appropriate handlers based on definition type
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { registry } from "../registry/registry";
|
|
7
|
+
import { resolve } from "../registry/resolver";
|
|
8
|
+
import type {
|
|
9
|
+
ActionDefinition,
|
|
10
|
+
Definition,
|
|
11
|
+
ExecutionResult,
|
|
12
|
+
ModelDefinition,
|
|
13
|
+
RunOptions,
|
|
14
|
+
SkillDefinition,
|
|
15
|
+
} from "../schema/types";
|
|
16
|
+
import { validateAndPrepare } from "../schema/validator";
|
|
17
|
+
import { jobRunner } from "./job";
|
|
18
|
+
import { pipelineRunner } from "./pipeline";
|
|
19
|
+
|
|
20
|
+
export class Executor {
|
|
21
|
+
/**
|
|
22
|
+
* Run a definition by name
|
|
23
|
+
*/
|
|
24
|
+
async run(
|
|
25
|
+
name: string,
|
|
26
|
+
inputs: Record<string, unknown>,
|
|
27
|
+
options?: RunOptions,
|
|
28
|
+
): Promise<ExecutionResult> {
|
|
29
|
+
// Resolve name to definition
|
|
30
|
+
const result = resolve(name, { required: true });
|
|
31
|
+
const definition = result.definition;
|
|
32
|
+
if (!definition) {
|
|
33
|
+
throw new Error(`Definition not found: ${name}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Validate and prepare inputs using Zod schema
|
|
37
|
+
const validation = validateAndPrepare(inputs, definition.schema.input);
|
|
38
|
+
if (!validation.valid) {
|
|
39
|
+
const errorMessages = validation.errors
|
|
40
|
+
.map((e) => (e.path ? `${e.path}: ${e.message}` : e.message))
|
|
41
|
+
.join(", ");
|
|
42
|
+
throw new Error(`Validation failed: ${errorMessages}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Use validated and transformed data (Zod applies defaults)
|
|
46
|
+
const validatedInputs = validation.data as Record<string, unknown>;
|
|
47
|
+
|
|
48
|
+
// Route to appropriate handler
|
|
49
|
+
switch (definition.type) {
|
|
50
|
+
case "model":
|
|
51
|
+
return this.runModel(definition, validatedInputs, options);
|
|
52
|
+
case "action":
|
|
53
|
+
return this.runAction(definition, validatedInputs, options);
|
|
54
|
+
case "skill":
|
|
55
|
+
return this.runSkill(definition, validatedInputs, options);
|
|
56
|
+
default:
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Unknown definition type: ${(definition as Definition).type}`,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Run a model directly
|
|
65
|
+
*/
|
|
66
|
+
async runModel(
|
|
67
|
+
model: ModelDefinition,
|
|
68
|
+
inputs: Record<string, unknown>,
|
|
69
|
+
options?: RunOptions,
|
|
70
|
+
): Promise<ExecutionResult> {
|
|
71
|
+
// Determine provider
|
|
72
|
+
const providerName = options?.provider ?? model.defaultProvider;
|
|
73
|
+
const provider = registry.getProvider(providerName);
|
|
74
|
+
|
|
75
|
+
if (!provider) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`Provider not found: ${providerName}. Available: ${registry
|
|
78
|
+
.listProviders()
|
|
79
|
+
.map((p) => p.name)
|
|
80
|
+
.join(", ")}`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Get provider-specific model ID
|
|
85
|
+
const providerModelId = model.providerModels?.[providerName] ?? model.name;
|
|
86
|
+
|
|
87
|
+
// Run via job runner
|
|
88
|
+
return jobRunner.run({
|
|
89
|
+
provider,
|
|
90
|
+
model: providerModelId,
|
|
91
|
+
inputs,
|
|
92
|
+
options,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Run an action
|
|
98
|
+
* Actions can route to models or have direct execution
|
|
99
|
+
*/
|
|
100
|
+
async runAction(
|
|
101
|
+
action: ActionDefinition,
|
|
102
|
+
inputs: Record<string, unknown>,
|
|
103
|
+
options?: RunOptions,
|
|
104
|
+
): Promise<ExecutionResult> {
|
|
105
|
+
// If action has direct execute function, use it
|
|
106
|
+
if (action.execute) {
|
|
107
|
+
const startTime = Date.now();
|
|
108
|
+
const output = await action.execute(inputs);
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
output: output as string | Record<string, unknown>,
|
|
112
|
+
duration: Date.now() - startTime,
|
|
113
|
+
provider: "local",
|
|
114
|
+
model: action.name,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Otherwise, route to a model
|
|
119
|
+
const route = this.selectRoute(action, inputs, options);
|
|
120
|
+
|
|
121
|
+
if (!route) {
|
|
122
|
+
throw new Error(`No valid route found for action: ${action.name}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Transform inputs if needed
|
|
126
|
+
const transformedInputs = route.transform
|
|
127
|
+
? route.transform(inputs)
|
|
128
|
+
: inputs;
|
|
129
|
+
|
|
130
|
+
// Recursive call to run the target
|
|
131
|
+
return this.run(route.target, transformedInputs, options);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Run a skill (multi-step pipeline)
|
|
136
|
+
*/
|
|
137
|
+
async runSkill(
|
|
138
|
+
skill: SkillDefinition,
|
|
139
|
+
inputs: Record<string, unknown>,
|
|
140
|
+
options?: RunOptions,
|
|
141
|
+
): Promise<ExecutionResult> {
|
|
142
|
+
return pipelineRunner.run(
|
|
143
|
+
skill,
|
|
144
|
+
inputs,
|
|
145
|
+
(name, stepInputs, stepOptions) =>
|
|
146
|
+
this.run(name, stepInputs, stepOptions),
|
|
147
|
+
options,
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Select the best route for an action based on inputs and conditions
|
|
153
|
+
*/
|
|
154
|
+
private selectRoute(
|
|
155
|
+
action: ActionDefinition,
|
|
156
|
+
inputs: Record<string, unknown>,
|
|
157
|
+
_options?: RunOptions,
|
|
158
|
+
) {
|
|
159
|
+
// Filter routes by conditions
|
|
160
|
+
const validRoutes = action.routes.filter((route) => {
|
|
161
|
+
if (!route.when) return true;
|
|
162
|
+
|
|
163
|
+
// Check each condition
|
|
164
|
+
for (const [key, expected] of Object.entries(route.when)) {
|
|
165
|
+
const actual = inputs[key];
|
|
166
|
+
|
|
167
|
+
// Handle special condition operators
|
|
168
|
+
if (typeof expected === "object" && expected !== null) {
|
|
169
|
+
const cond = expected as Record<string, unknown>;
|
|
170
|
+
|
|
171
|
+
if ("$lt" in cond && !(Number(actual) < Number(cond.$lt)))
|
|
172
|
+
return false;
|
|
173
|
+
if ("$lte" in cond && !(Number(actual) <= Number(cond.$lte)))
|
|
174
|
+
return false;
|
|
175
|
+
if ("$gt" in cond && !(Number(actual) > Number(cond.$gt)))
|
|
176
|
+
return false;
|
|
177
|
+
if ("$gte" in cond && !(Number(actual) >= Number(cond.$gte)))
|
|
178
|
+
return false;
|
|
179
|
+
if ("$eq" in cond && actual !== cond.$eq) return false;
|
|
180
|
+
if ("$ne" in cond && actual === cond.$ne) return false;
|
|
181
|
+
if ("$in" in cond && !Array.isArray(cond.$in)) return false;
|
|
182
|
+
if ("$in" in cond && !(cond.$in as unknown[]).includes(actual))
|
|
183
|
+
return false;
|
|
184
|
+
} else if (actual !== expected) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return true;
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Sort by priority (higher = preferred)
|
|
193
|
+
validRoutes.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
194
|
+
|
|
195
|
+
// Return the highest priority route
|
|
196
|
+
return validRoutes[0];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Global executor instance
|
|
201
|
+
export const executor = new Executor();
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Executor module exports
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { Executor, executor } from "./executor";
|
|
6
|
+
export type { JobConfig } from "./job";
|
|
7
|
+
export { JobRunner, jobRunner } from "./job";
|
|
8
|
+
export type {
|
|
9
|
+
PipelineContext,
|
|
10
|
+
PipelineOptions,
|
|
11
|
+
StepExecutor,
|
|
12
|
+
} from "./pipeline";
|
|
13
|
+
export { PipelineRunner, pipelineRunner } from "./pipeline";
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Job State Machine
|
|
3
|
+
* Manages job lifecycle: queued -> processing -> completed/failed
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
ExecutionResult,
|
|
8
|
+
Job,
|
|
9
|
+
JobStatus,
|
|
10
|
+
JobStatusUpdate,
|
|
11
|
+
Provider,
|
|
12
|
+
RunOptions,
|
|
13
|
+
} from "../schema/types";
|
|
14
|
+
|
|
15
|
+
export interface JobConfig {
|
|
16
|
+
provider: Provider;
|
|
17
|
+
model: string;
|
|
18
|
+
inputs: Record<string, unknown>;
|
|
19
|
+
options?: RunOptions;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class JobRunner {
|
|
23
|
+
private jobs = new Map<string, Job>();
|
|
24
|
+
private pollInterval = 2000; // Start with 2s
|
|
25
|
+
private maxPollInterval = 10000; // Cap at 10s
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Create and run a job
|
|
29
|
+
*/
|
|
30
|
+
async run(config: JobConfig): Promise<ExecutionResult> {
|
|
31
|
+
const { provider, model, inputs, options } = config;
|
|
32
|
+
const startTime = Date.now();
|
|
33
|
+
|
|
34
|
+
// Create job record
|
|
35
|
+
const job: Job = {
|
|
36
|
+
id: "",
|
|
37
|
+
status: "pending",
|
|
38
|
+
provider: provider.name,
|
|
39
|
+
model,
|
|
40
|
+
inputs,
|
|
41
|
+
createdAt: new Date(),
|
|
42
|
+
updatedAt: new Date(),
|
|
43
|
+
logs: [],
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
// Submit to provider
|
|
48
|
+
job.status = "queued";
|
|
49
|
+
job.id = await provider.submit(model, inputs);
|
|
50
|
+
this.jobs.set(job.id, job);
|
|
51
|
+
|
|
52
|
+
if (options?.onStatusChange) {
|
|
53
|
+
options.onStatusChange("queued");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// If not waiting, return immediately
|
|
57
|
+
if (options?.wait === false) {
|
|
58
|
+
return {
|
|
59
|
+
output: { jobId: job.id },
|
|
60
|
+
duration: Date.now() - startTime,
|
|
61
|
+
provider: provider.name,
|
|
62
|
+
model,
|
|
63
|
+
jobId: job.id,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Poll for completion
|
|
68
|
+
const result = await this.waitForCompletion(job, provider, options);
|
|
69
|
+
|
|
70
|
+
// Update job record
|
|
71
|
+
job.status = "completed";
|
|
72
|
+
job.output = result;
|
|
73
|
+
job.completedAt = new Date();
|
|
74
|
+
job.updatedAt = new Date();
|
|
75
|
+
|
|
76
|
+
const duration = Date.now() - startTime;
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
output: result as string | Record<string, unknown>,
|
|
80
|
+
duration,
|
|
81
|
+
provider: provider.name,
|
|
82
|
+
model,
|
|
83
|
+
jobId: job.id,
|
|
84
|
+
};
|
|
85
|
+
} catch (error) {
|
|
86
|
+
job.status = "failed";
|
|
87
|
+
job.error = error instanceof Error ? error.message : String(error);
|
|
88
|
+
job.updatedAt = new Date();
|
|
89
|
+
|
|
90
|
+
if (options?.onStatusChange) {
|
|
91
|
+
options.onStatusChange("failed");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Wait for a job to complete with polling
|
|
100
|
+
*/
|
|
101
|
+
private async waitForCompletion(
|
|
102
|
+
job: Job,
|
|
103
|
+
provider: Provider,
|
|
104
|
+
options?: RunOptions,
|
|
105
|
+
): Promise<unknown> {
|
|
106
|
+
const timeout = options?.timeout ?? 300000; // 5 minutes default
|
|
107
|
+
const startTime = Date.now();
|
|
108
|
+
let currentInterval = this.pollInterval;
|
|
109
|
+
|
|
110
|
+
while (Date.now() - startTime < timeout) {
|
|
111
|
+
const status = await provider.getStatus(job.id);
|
|
112
|
+
|
|
113
|
+
// Update job record
|
|
114
|
+
job.status = this.mapStatus(status.status);
|
|
115
|
+
job.updatedAt = new Date();
|
|
116
|
+
|
|
117
|
+
if (status.progress !== undefined) {
|
|
118
|
+
job.progress = status.progress;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (status.logs) {
|
|
122
|
+
job.logs = [...(job.logs || []), ...status.logs];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Notify progress
|
|
126
|
+
if (options?.onProgress && status.progress !== undefined) {
|
|
127
|
+
options.onProgress(status.progress, status.logs);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (options?.onStatusChange) {
|
|
131
|
+
options.onStatusChange(job.status);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Check terminal states
|
|
135
|
+
if (status.status === "completed") {
|
|
136
|
+
return status.output ?? (await provider.getResult(job.id));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (status.status === "failed") {
|
|
140
|
+
throw new Error(status.error ?? "Job failed");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (status.status === "cancelled") {
|
|
144
|
+
throw new Error("Job was cancelled");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Wait with exponential backoff
|
|
148
|
+
await this.sleep(currentInterval);
|
|
149
|
+
currentInterval = Math.min(currentInterval * 1.5, this.maxPollInterval);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
throw new Error(`Job ${job.id} timed out after ${timeout}ms`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Map provider status to our status
|
|
157
|
+
*/
|
|
158
|
+
private mapStatus(status: JobStatusUpdate["status"]): JobStatus {
|
|
159
|
+
return status;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Get a job by ID
|
|
164
|
+
*/
|
|
165
|
+
get(jobId: string): Job | undefined {
|
|
166
|
+
return this.jobs.get(jobId);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Cancel a job
|
|
171
|
+
*/
|
|
172
|
+
async cancel(jobId: string, provider: Provider): Promise<void> {
|
|
173
|
+
const job = this.jobs.get(jobId);
|
|
174
|
+
if (!job) {
|
|
175
|
+
throw new Error(`Job not found: ${jobId}`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (provider.cancel) {
|
|
179
|
+
await provider.cancel(jobId);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
job.status = "cancelled";
|
|
183
|
+
job.updatedAt = new Date();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* List all jobs
|
|
188
|
+
*/
|
|
189
|
+
list(): Job[] {
|
|
190
|
+
return Array.from(this.jobs.values());
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Clear completed jobs
|
|
195
|
+
*/
|
|
196
|
+
clearCompleted(): void {
|
|
197
|
+
for (const [id, job] of this.jobs) {
|
|
198
|
+
if (
|
|
199
|
+
job.status === "completed" ||
|
|
200
|
+
job.status === "failed" ||
|
|
201
|
+
job.status === "cancelled"
|
|
202
|
+
) {
|
|
203
|
+
this.jobs.delete(id);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
private sleep(ms: number): Promise<void> {
|
|
209
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Global job runner instance
|
|
214
|
+
export const jobRunner = new JobRunner();
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pipeline Runner
|
|
3
|
+
* Executes multi-step skills/workflows
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
ExecutionResult,
|
|
8
|
+
RunOptions,
|
|
9
|
+
SkillDefinition,
|
|
10
|
+
SkillStep,
|
|
11
|
+
} from "../schema/types";
|
|
12
|
+
|
|
13
|
+
export interface PipelineContext {
|
|
14
|
+
/** Initial inputs provided to the pipeline */
|
|
15
|
+
inputs: Record<string, unknown>;
|
|
16
|
+
/** Results from each completed step, keyed by step name */
|
|
17
|
+
results: Record<string, unknown>;
|
|
18
|
+
/** Current step index */
|
|
19
|
+
stepIndex: number;
|
|
20
|
+
/** Total number of steps */
|
|
21
|
+
totalSteps: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface PipelineOptions extends RunOptions {
|
|
25
|
+
/** If true, stop on first error */
|
|
26
|
+
stopOnError?: boolean;
|
|
27
|
+
/** Step-level callback */
|
|
28
|
+
onStepStart?: (step: SkillStep, context: PipelineContext) => void;
|
|
29
|
+
/** Step completion callback */
|
|
30
|
+
onStepComplete?: (
|
|
31
|
+
step: SkillStep,
|
|
32
|
+
result: unknown,
|
|
33
|
+
context: PipelineContext,
|
|
34
|
+
) => void;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type StepExecutor = (
|
|
38
|
+
name: string,
|
|
39
|
+
inputs: Record<string, unknown>,
|
|
40
|
+
options?: RunOptions,
|
|
41
|
+
) => Promise<ExecutionResult>;
|
|
42
|
+
|
|
43
|
+
export class PipelineRunner {
|
|
44
|
+
/**
|
|
45
|
+
* Execute a skill definition
|
|
46
|
+
*/
|
|
47
|
+
async run(
|
|
48
|
+
skill: SkillDefinition,
|
|
49
|
+
inputs: Record<string, unknown>,
|
|
50
|
+
executor: StepExecutor,
|
|
51
|
+
options?: PipelineOptions,
|
|
52
|
+
): Promise<ExecutionResult> {
|
|
53
|
+
const startTime = Date.now();
|
|
54
|
+
const context: PipelineContext = {
|
|
55
|
+
inputs,
|
|
56
|
+
results: {},
|
|
57
|
+
stepIndex: 0,
|
|
58
|
+
totalSteps: skill.steps.length,
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
console.log(`[pipeline] starting skill: ${skill.name}`);
|
|
62
|
+
console.log(`[pipeline] steps: ${skill.steps.length}`);
|
|
63
|
+
|
|
64
|
+
let lastResult: ExecutionResult | null = null;
|
|
65
|
+
|
|
66
|
+
for (let i = 0; i < skill.steps.length; i++) {
|
|
67
|
+
const step = skill.steps[i];
|
|
68
|
+
if (!step) continue;
|
|
69
|
+
|
|
70
|
+
context.stepIndex = i;
|
|
71
|
+
|
|
72
|
+
// Check step condition
|
|
73
|
+
if (step.when && !this.evaluateCondition(step.when, context)) {
|
|
74
|
+
console.log(
|
|
75
|
+
`[pipeline] skipping step ${i + 1}: ${step.name} (condition not met)`,
|
|
76
|
+
);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log(
|
|
81
|
+
`[pipeline] step ${i + 1}/${skill.steps.length}: ${step.name}`,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
if (options?.onStepStart) {
|
|
85
|
+
options.onStepStart(step, context);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
// Resolve inputs for this step
|
|
90
|
+
const stepInputs = this.resolveInputs(step.inputs, context);
|
|
91
|
+
|
|
92
|
+
// Execute the step
|
|
93
|
+
const result = await executor(step.run, stepInputs, options);
|
|
94
|
+
|
|
95
|
+
// Store result
|
|
96
|
+
context.results[step.name] = result.output;
|
|
97
|
+
lastResult = result;
|
|
98
|
+
|
|
99
|
+
if (options?.onStepComplete) {
|
|
100
|
+
options.onStepComplete(step, result, context);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
console.log(`[pipeline] step ${step.name} completed`);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error(`[pipeline] step ${step.name} failed:`, error);
|
|
106
|
+
|
|
107
|
+
if (options?.stopOnError !== false) {
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Store error as result
|
|
112
|
+
context.results[step.name] = {
|
|
113
|
+
error: error instanceof Error ? error.message : String(error),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const duration = Date.now() - startTime;
|
|
119
|
+
console.log(`[pipeline] skill ${skill.name} completed in ${duration}ms`);
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
output: lastResult?.output ?? context.results,
|
|
123
|
+
duration,
|
|
124
|
+
provider: lastResult?.provider ?? "pipeline",
|
|
125
|
+
model: skill.name,
|
|
126
|
+
metadata: {
|
|
127
|
+
stepResults: context.results,
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Evaluate a condition against the current context
|
|
134
|
+
*/
|
|
135
|
+
private evaluateCondition(
|
|
136
|
+
condition: Record<string, unknown>,
|
|
137
|
+
context: PipelineContext,
|
|
138
|
+
): boolean {
|
|
139
|
+
for (const [key, expected] of Object.entries(condition)) {
|
|
140
|
+
const actual = this.resolveValue(key, context);
|
|
141
|
+
|
|
142
|
+
// Simple equality check
|
|
143
|
+
if (actual !== expected) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Resolve inputs for a step, substituting references
|
|
152
|
+
*/
|
|
153
|
+
private resolveInputs(
|
|
154
|
+
inputs: Record<string, unknown>,
|
|
155
|
+
context: PipelineContext,
|
|
156
|
+
): Record<string, unknown> {
|
|
157
|
+
const resolved: Record<string, unknown> = {};
|
|
158
|
+
|
|
159
|
+
for (const [key, value] of Object.entries(inputs)) {
|
|
160
|
+
resolved[key] = this.resolveValue(value, context);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return resolved;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Resolve a value, handling references like $inputs.foo or $results.step1.output
|
|
168
|
+
*/
|
|
169
|
+
private resolveValue(value: unknown, context: PipelineContext): unknown {
|
|
170
|
+
if (typeof value !== "string") {
|
|
171
|
+
return value;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Check for reference syntax
|
|
175
|
+
if (!value.startsWith("$")) {
|
|
176
|
+
return value;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const path = value.slice(1).split(".");
|
|
180
|
+
const source = path[0];
|
|
181
|
+
const rest = path.slice(1);
|
|
182
|
+
|
|
183
|
+
if (!source) {
|
|
184
|
+
return value;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
let current: unknown;
|
|
188
|
+
|
|
189
|
+
switch (source) {
|
|
190
|
+
case "inputs":
|
|
191
|
+
current = context.inputs;
|
|
192
|
+
break;
|
|
193
|
+
case "results":
|
|
194
|
+
current = context.results;
|
|
195
|
+
break;
|
|
196
|
+
default:
|
|
197
|
+
// Check if it's a direct step result reference
|
|
198
|
+
if (source in context.results) {
|
|
199
|
+
current = context.results[source];
|
|
200
|
+
} else {
|
|
201
|
+
return value; // Return as-is if not a valid reference
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Navigate the path
|
|
206
|
+
for (const part of rest) {
|
|
207
|
+
if (current === null || current === undefined) {
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
if (typeof current === "object") {
|
|
211
|
+
current = (current as Record<string, unknown>)[part];
|
|
212
|
+
} else {
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return current;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Global pipeline runner instance
|
|
222
|
+
export const pipelineRunner = new PipelineRunner();
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Registry module exports
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type { LoaderOptions } from "./loader";
|
|
6
|
+
export { loadDefinitions, loadUserSkills, reloadDefinitions } from "./loader";
|
|
7
|
+
export { Registry, registry } from "./registry";
|
|
8
|
+
export type { ResolveOptions, ResolveResult } from "./resolver";
|
|
9
|
+
export { findSimilar, resolve, search, suggest } from "./resolver";
|