@limo-labs/deity 0.2.1 → 0.3.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -64,7 +64,8 @@ var init_compile_observe = __esm({
64
64
 
65
65
  // src/components/Agent.tsx
66
66
  function Agent(props) {
67
- const { id, input, output, description, tags, loopValidator, loopConfig, tools: propsTools, children } = props;
67
+ const { id, input, output, description, tags, loopValidator, loopConfig, tools: propsTools, mode, children } = props;
68
+ const resolvedMode = mode ?? "llm";
68
69
  if (!id) {
69
70
  throw new Error("Agent: id is required");
70
71
  }
@@ -75,13 +76,31 @@ function Agent(props) {
75
76
  throw new Error("Agent: output schema is required");
76
77
  }
77
78
  if (!children || children.length === 0) {
79
+ if (resolvedMode === "pure") {
80
+ throw new Error("Agent: pure mode must have at least a Result child");
81
+ }
78
82
  throw new Error("Agent: must have at least Prompt and Result children");
79
83
  }
80
84
  const toolsNode = children.find((child) => child?.type === "Tools");
81
85
  const nonToolsChildren = children.filter((child) => child?.type !== "Tools");
82
- const [prompt, ...rest] = nonToolsChildren;
83
- if (!prompt || prompt.type !== "Prompt") {
84
- throw new Error("Agent: first non-Tools child must be a Prompt component");
86
+ let prompt;
87
+ let rest;
88
+ if (resolvedMode === "pure") {
89
+ const firstNonTools = nonToolsChildren[0];
90
+ if (firstNonTools?.type === "Prompt") {
91
+ prompt = firstNonTools;
92
+ rest = nonToolsChildren.slice(1);
93
+ } else {
94
+ prompt = void 0;
95
+ rest = nonToolsChildren;
96
+ }
97
+ } else {
98
+ const [first, ...remaining] = nonToolsChildren;
99
+ if (!first || first.type !== "Prompt") {
100
+ throw new Error("Agent: first non-Tools child must be a Prompt component");
101
+ }
102
+ prompt = first;
103
+ rest = remaining;
85
104
  }
86
105
  const result = rest.find((child) => child?.type === "Result");
87
106
  if (!result) {
@@ -91,9 +110,8 @@ function Agent(props) {
91
110
  const validate = rest.find((child) => child?.type === "Validate");
92
111
  const retry = rest.find((child) => child?.type === "Retry");
93
112
  const mergedTools = mergeTools(propsTools, toolsNode);
94
- const orderedChildren = [
95
- prompt
96
- ];
113
+ const orderedChildren = [];
114
+ if (prompt) orderedChildren.push(prompt);
97
115
  if (observe) orderedChildren.push(observe);
98
116
  orderedChildren.push(result);
99
117
  if (validate) orderedChildren.push(validate);
@@ -109,7 +127,8 @@ function Agent(props) {
109
127
  tags,
110
128
  loopValidator,
111
129
  loopConfig,
112
- tools: mergedTools
130
+ tools: mergedTools,
131
+ mode: resolvedMode
113
132
  },
114
133
  children: orderedChildren
115
134
  };
@@ -1079,13 +1098,14 @@ function compileAgent(ast) {
1079
1098
  const resultNode = children.find((n) => n?.type === "Result");
1080
1099
  const validateNode = children.find((n) => n?.type === "Validate");
1081
1100
  const retryNode = children.find((n) => n?.type === "Retry");
1082
- if (!promptNode) {
1101
+ const mode = ast.props.mode ?? "llm";
1102
+ if (mode !== "pure" && !promptNode) {
1083
1103
  throw new Error("Agent must have a Prompt node");
1084
1104
  }
1085
1105
  if (!resultNode) {
1086
1106
  throw new Error("Agent must have a Result node");
1087
1107
  }
1088
- const buildPrompt = compilePrompt(promptNode);
1108
+ const buildPrompt = promptNode ? compilePrompt(promptNode) : () => [];
1089
1109
  const observe = observeNode ? compileObserve(observeNode) : void 0;
1090
1110
  const extractOutput = compileResult(resultNode, observe);
1091
1111
  const validateOutput = validateNode ? compileValidate(validateNode) : void 0;
@@ -1097,6 +1117,9 @@ function compileAgent(ast) {
1097
1117
  buildPrompt,
1098
1118
  extractOutput
1099
1119
  };
1120
+ if (mode === "pure") {
1121
+ component.mode = "pure";
1122
+ }
1100
1123
  if (validateOutput) {
1101
1124
  component.validateOutput = validateOutput;
1102
1125
  }
@@ -1237,6 +1260,9 @@ function createLazyAgentComponent(astNode) {
1237
1260
  get loopConfig() {
1238
1261
  return astNode.props.loopConfig;
1239
1262
  },
1263
+ get mode() {
1264
+ return astNode.props.mode;
1265
+ },
1240
1266
  // Lazy methods - compile on first call
1241
1267
  buildPrompt(ctx) {
1242
1268
  return ensureCompiled().buildPrompt(ctx);
@@ -2876,6 +2902,7 @@ var DEFAULT_LLM_LOOP_CONFIG = {
2876
2902
  // 2 minutes
2877
2903
  verbose: false
2878
2904
  };
2905
+ var DEADLINE_PROMPT = `\u26A0\uFE0F DEADLINE REACHED: You have reached the maximum allowed rounds or time limit. You MUST immediately provide your final answer based on the information gathered so far. Do NOT call any more tools. Respond with your best final output NOW.`;
2879
2906
  async function executeLLMLoop(adapter, initialMessages, tools, config, ctx, loopConfig = {}, validator) {
2880
2907
  const cfg = { ...DEFAULT_LLM_LOOP_CONFIG, ...loopConfig };
2881
2908
  const messagesWithMemory = await injectRelevantMemories(
@@ -2889,6 +2916,7 @@ async function executeLLMLoop(adapter, initialMessages, tools, config, ctx, loop
2889
2916
  let toolCallsExecuted = 0;
2890
2917
  let response;
2891
2918
  let toolCallsThisRound = [];
2919
+ const startTime = Date.now();
2892
2920
  while (rounds < cfg.maxToolRounds) {
2893
2921
  rounds++;
2894
2922
  toolCallsThisRound = [];
@@ -2936,10 +2964,6 @@ async function executeLLMLoop(adapter, initialMessages, tools, config, ctx, loop
2936
2964
  }
2937
2965
  break;
2938
2966
  }
2939
- if (rounds >= cfg.maxToolRounds) {
2940
- errors.push(`Maximum tool rounds (${cfg.maxToolRounds}) exceeded`);
2941
- break;
2942
- }
2943
2967
  for (const toolCall of response.toolCalls) {
2944
2968
  const alreadyExecuted = toolCall._alreadyExecuted;
2945
2969
  if (alreadyExecuted) {
@@ -3007,6 +3031,23 @@ async function executeLLMLoop(adapter, initialMessages, tools, config, ctx, loop
3007
3031
  if (allPreExecuted) {
3008
3032
  break;
3009
3033
  }
3034
+ const isTimeout = cfg.timeout > 0 && Date.now() - startTime >= cfg.timeout;
3035
+ const isLastRound = rounds >= cfg.maxToolRounds;
3036
+ if (isTimeout || isLastRound) {
3037
+ messages.push({
3038
+ role: "user",
3039
+ content: DEADLINE_PROMPT
3040
+ });
3041
+ try {
3042
+ response = await adapter.generate(messages, void 0, config, ctx);
3043
+ messages.push({ role: "assistant", content: response.content });
3044
+ } catch (_error) {
3045
+ }
3046
+ errors.push(
3047
+ isTimeout ? `Timeout reached (${cfg.timeout}ms)` : `Maximum tool rounds (${cfg.maxToolRounds}) reached`
3048
+ );
3049
+ break;
3050
+ }
3010
3051
  }
3011
3052
  return {
3012
3053
  response,
@@ -3246,6 +3287,17 @@ async function executeWithRetry(component, ctx, executeFn) {
3246
3287
  }
3247
3288
  const shouldRetry = retryConfig.shouldRetry ? retryConfig.shouldRetry(attempts, output, ctx) : attempts < retryConfig.maxAttempts;
3248
3289
  if (!shouldRetry) {
3290
+ const lastOutput = outputHistory[outputHistory.length - 1];
3291
+ if (lastOutput !== void 0) {
3292
+ return {
3293
+ output: lastOutput,
3294
+ success: true,
3295
+ attempts,
3296
+ errors: validation.errors ?? ["Validation failed (used best-effort output)"],
3297
+ validationHistory,
3298
+ outputHistory
3299
+ };
3300
+ }
3249
3301
  return {
3250
3302
  success: false,
3251
3303
  attempts,
@@ -3330,6 +3382,13 @@ function parseComponentOutput(content, schema) {
3330
3382
  }
3331
3383
 
3332
3384
  // src/engine/executor.ts
3385
+ var EMPTY_LLM_RESULT = {
3386
+ response: { content: "" },
3387
+ messages: [],
3388
+ rounds: 0,
3389
+ toolCallsExecuted: 0,
3390
+ errors: []
3391
+ };
3333
3392
  async function executeComponent(component, ctx, adapter, config) {
3334
3393
  const startTime = Date.now();
3335
3394
  const llmResults = [];
@@ -4344,7 +4403,7 @@ zod.z.object({
4344
4403
  sessionId: zod.z.string().min(1, "Session ID cannot be empty"),
4345
4404
  workflowName: zod.z.string().min(1, "Workflow name cannot be empty"),
4346
4405
  pausedAt: zod.z.string().datetime("Must be ISO 8601 datetime"),
4347
- version: zod.z.string().regex(/^\d+\.\d+\.\d+$/, "Must be semver format (e.g., 4.0.0)"),
4406
+ version: zod.z.string().regex(/^\d+\.\d+\.\d+$/, "Must be semver format (e.g., 1.0.0)"),
4348
4407
  state: PausedSessionStateSchema,
4349
4408
  metadata: PausedSessionMetadataSchema
4350
4409
  });
@@ -4360,7 +4419,7 @@ var SessionSerializer = class {
4360
4419
  metadata;
4361
4420
  constructor() {
4362
4421
  this.metadata = {
4363
- version: "4.0.0",
4422
+ version: "1.0.0",
4364
4423
  timestamp: /* @__PURE__ */ new Date(),
4365
4424
  specialFields: []
4366
4425
  };
@@ -5030,6 +5089,9 @@ async function executeStepNode(node, ctx, adapter, config) {
5030
5089
  if (!node.component) {
5031
5090
  throw new Error("Step node missing component");
5032
5091
  }
5092
+ if (node.component.mode === "pure") {
5093
+ return executePureComponent(node.component, ctx);
5094
+ }
5033
5095
  if (!adapter) {
5034
5096
  if (node.component.model?.adapter) {
5035
5097
  adapter = node.component.model.adapter;
@@ -5217,6 +5279,56 @@ function createIterationContext(baseCtx, currentItem, index, totalItems, itemMod
5217
5279
  isLastIteration: index === totalItems - 1
5218
5280
  };
5219
5281
  }
5282
+ async function executePureComponent(component, ctx) {
5283
+ ctx.ui?.startStep(component.id, component.id);
5284
+ await ctx.trace.log({
5285
+ type: "stage_start",
5286
+ stageId: component.id,
5287
+ timestamp: /* @__PURE__ */ new Date()
5288
+ });
5289
+ try {
5290
+ if (!component.extractOutput) {
5291
+ throw new Error(`Pure component '${component.id}' must have extractOutput defined`);
5292
+ }
5293
+ const output = await Promise.resolve(
5294
+ component.extractOutput(ctx, EMPTY_LLM_RESULT)
5295
+ );
5296
+ const schemaResult = component.outputSchema.safeParse(output);
5297
+ if (!schemaResult.success) {
5298
+ const errors = schemaResult.error.issues.map((e) => `${e.path.join(".")}: ${e.message}`).join("; ");
5299
+ throw new Error(`Pure component '${component.id}' output validation failed: ${errors}`);
5300
+ }
5301
+ const validatedOutput = schemaResult.data;
5302
+ if (component.validateOutput) {
5303
+ const validationResult = await Promise.resolve(
5304
+ component.validateOutput(validatedOutput, ctx)
5305
+ );
5306
+ if (!validationResult.valid) {
5307
+ const feedback = validationResult.feedback || validationResult.errors?.join("; ") || "Validation failed";
5308
+ throw new Error(`Pure component '${component.id}' validateOutput failed: ${feedback}`);
5309
+ }
5310
+ }
5311
+ if (ctx instanceof ExecutionContext) {
5312
+ ctx.previousOutputs[component.id] = validatedOutput;
5313
+ }
5314
+ await ctx.trace.log({
5315
+ type: "stage_complete",
5316
+ output: validatedOutput,
5317
+ timestamp: /* @__PURE__ */ new Date()
5318
+ });
5319
+ ctx.ui?.completeStep(component.id, "Pure execution complete");
5320
+ return validatedOutput;
5321
+ } catch (error) {
5322
+ const errorMessage = error instanceof Error ? error.message : String(error);
5323
+ await ctx.trace.log({
5324
+ type: "stage_failed",
5325
+ error: errorMessage,
5326
+ timestamp: /* @__PURE__ */ new Date()
5327
+ });
5328
+ ctx.ui?.failStep(component.id, errorMessage);
5329
+ throw error;
5330
+ }
5331
+ }
5220
5332
  function createStepNode(component) {
5221
5333
  return {
5222
5334
  type: "step",
@@ -5252,8 +5364,10 @@ function createLoopNode(child, maxIterations) {
5252
5364
 
5253
5365
  exports.Agent = Agent;
5254
5366
  exports.Conditional = Conditional;
5367
+ exports.DEADLINE_PROMPT = DEADLINE_PROMPT;
5255
5368
  exports.DEBUG_ENABLED = DEBUG_ENABLED;
5256
5369
  exports.DebugLogger = DebugLogger;
5370
+ exports.EMPTY_LLM_RESULT = EMPTY_LLM_RESULT;
5257
5371
  exports.ForEach = ForEach;
5258
5372
  exports.InMemoryStore = InMemoryStore;
5259
5373
  exports.InMemoryTrace = InMemoryTrace;