@limo-labs/deity 0.2.1 → 0.2.2

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/README.md DELETED
@@ -1,681 +0,0 @@
1
- # @limo-labs/deity
2
-
3
- > **Declarative AI Agent Framework** — Build type-safe AI agents with JSX/TSX syntax for agents and declarative API for workflows
4
-
5
- Deity is a declarative framework for building AI agents. **Agents** use familiar JSX/TSX syntax like React components. **Workflows** use declarative function composition. Full TypeScript support and zero runtime overhead.
6
-
7
- **Important Distinction:**
8
- - **Agent Definition:** Uses true JSX/TSX tags (`<Agent>`, `<Prompt>`) in `.tsx` files
9
- - **Workflow Definition:** Uses function calls (`Workflow()`, `Sequence()`) in `.ts` files
10
-
11
- [![npm version](https://img.shields.io/npm/v/@limo-labs/deity.svg)](https://www.npmjs.com/package/@limo-labs/deity)
12
- [![License](https://img.shields.io/npm/l/@limo-labs/deity.svg)](https://github.com/Limo-Labs/Limo-Deity/blob/main/LICENSE)
13
- [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
14
-
15
- ## ✨ Features
16
-
17
- - 🎨 **JSX/TSX Syntax for Agents** - Write agents with familiar React-like JSX tags (`.tsx` files)
18
- - 📦 **Declarative Workflow API** - Compose workflows with function calls (`.ts` files)
19
- - 🔒 **Type Safe** - Full TypeScript support with Zod schema validation
20
- - 🔧 **Declarative** - Compose agents and workflows from reusable components
21
- - 🚀 **Zero Runtime Overhead** - Compiles to lightweight AST nodes
22
- - 🎯 **LLM Agnostic** - Works with any LLM adapter
23
- - 🔁 **Auto Retry** - Built-in retry logic with LLM feedback
24
- - ✅ **Output Validation** - Declarative validation rules
25
- - 🛠️ **Tool Support** - Native tool calling integration
26
- - 📊 **Observability** - Built-in execution tracing
27
-
28
- ## 📦 Installation
29
-
30
- ```bash
31
- npm install @limo-labs/deity zod
32
- ```
33
-
34
- ```bash
35
- yarn add @limo-labs/deity zod
36
- ```
37
-
38
- ```bash
39
- pnpm add @limo-labs/deity zod
40
- ```
41
-
42
- ## 🚀 Quick Start
43
-
44
- ### 1. Configure TypeScript (Optional - For JSX/TSX Agent Syntax)
45
-
46
- If you want to use **JSX/TSX tags** for Agent definitions (`.tsx` files), add JSX support to your `tsconfig.json`:
47
-
48
- ```json
49
- {
50
- "compilerOptions": {
51
- "jsx": "react-jsx",
52
- "jsxImportSource": "@limo-labs/deity"
53
- }
54
- }
55
- ```
56
-
57
- **Note:** Workflow definitions use function calls and work in regular `.ts` files without JSX configuration.
58
-
59
- ### 2. Create Your First Agent (Using JSX/TSX Tags)
60
-
61
- **File:** `agent.tsx` (note the `.tsx` extension for JSX support)
62
-
63
- ```tsx
64
- import { Agent, Prompt, System, User, Result } from '@limo-labs/deity';
65
- import { z } from 'zod';
66
-
67
- // Define schemas
68
- const InputSchema = z.object({
69
- topic: z.string()
70
- });
71
-
72
- const OutputSchema = z.object({
73
- summary: z.string(),
74
- keyPoints: z.array(z.string())
75
- });
76
-
77
- // Create agent using JSX tags (requires .tsx file)
78
- const SummarizerAgent = (
79
- <Agent id="summarizer" input={InputSchema} output={OutputSchema}>
80
- <Prompt>
81
- <System>You are a summarization expert.</System>
82
- <User>{(ctx: any): string => `Summarize this topic: ${ctx.inputs.topic}`}</User>
83
- </Prompt>
84
- <Result>
85
- {(ctx: any, llmResult: any) => {
86
- // Extract output from LLM response
87
- const content = llmResult.response.content;
88
- return JSON.parse(content);
89
- }}
90
- </Result>
91
- </Agent>
92
- );
93
- ```
94
-
95
- ### 3. Execute the Agent
96
-
97
- ```typescript
98
- import { compileAgent, executeComponent, createEnhancedContext } from '@limo-labs/deity';
99
-
100
- // Compile JSX to executable component
101
- const component = compileAgent(SummarizerAgent);
102
-
103
- // Create execution context
104
- const ctx = await createEnhancedContext({
105
- inputs: { topic: 'Climate change' }
106
- });
107
-
108
- // Execute
109
- const result = await executeComponent(component, ctx, llmAdapter);
110
- console.log(result.output.summary);
111
- ```
112
-
113
- ## 📖 Core Concepts
114
-
115
- ### Components
116
-
117
- Deity provides declarative components for building agents:
118
-
119
- - **`<Agent>`** - Root component defining the agent
120
- - **`<Prompt>`** - Prompt construction
121
- - **`<System>`** - System message
122
- - **`<User>`** - User message
123
- - **`<Result>`** - Output extraction
124
- - **`<Validate>`** - Output validation
125
- - **`<Retry>`** - Retry configuration
126
- - **`<Observe>`** - LLM loop observation
127
-
128
- ### Agent Component
129
-
130
- The `<Agent>` component is the root of every agent:
131
-
132
- ```tsx
133
- <Agent
134
- id="unique-id"
135
- input={InputSchema}
136
- output={OutputSchema}
137
- loopConfig={{ maxToolRounds: 10, timeout: 30000 }}
138
- tools={[...]}
139
- >
140
- {/* Children components */}
141
- </Agent>
142
- ```
143
-
144
- **Props:**
145
- - `id` (required) - Unique agent identifier
146
- - `input` (required) - Zod schema for input validation
147
- - `output` (required) - Zod schema for output validation
148
- - `loopConfig` (optional) - LLM loop configuration
149
- - `loopValidator` (optional) - Custom loop validator
150
- - `tools` (optional) - Tool definitions
151
-
152
- ### Prompt Component
153
-
154
- The `<Prompt>` component defines how to build the LLM prompt:
155
-
156
- ```tsx
157
- <Prompt>
158
- <System>
159
- {async (): Promise<string> => {
160
- // Load prompt from file or construct dynamically
161
- return await loadPromptTemplate();
162
- }}
163
- </System>
164
- <User>
165
- {(ctx: any): string => `Process: ${ctx.inputs.task}`}
166
- </User>
167
- </Prompt>
168
- ```
169
-
170
- ### Result Component
171
-
172
- The `<Result>` component extracts structured output:
173
-
174
- ```tsx
175
- <Result>
176
- {(ctx: any, llmResult: any): OutputType => {
177
- // Extract from tool calls
178
- const toolCall = llmResult.response.toolCalls?.find(
179
- tc => tc.name === 'submit_result'
180
- );
181
-
182
- if (toolCall) {
183
- return JSON.parse(toolCall.arguments);
184
- }
185
-
186
- // Or extract from text content
187
- const match = llmResult.response.content.match(/```json\n(.*?)\n```/s);
188
- return JSON.parse(match[1]);
189
- }}
190
- </Result>
191
- ```
192
-
193
- ### Validate Component
194
-
195
- The `<Validate>` component defines validation rules:
196
-
197
- ```tsx
198
- <Validate>
199
- {(output: any, ctx: any) => ({
200
- rules: [
201
- {
202
- check: output.summary.length >= 100,
203
- error: 'Summary must be at least 100 characters'
204
- },
205
- {
206
- check: output.keyPoints.length >= 3,
207
- error: 'Must have at least 3 key points'
208
- }
209
- ]
210
- })}
211
- </Validate>
212
- ```
213
-
214
- ### Retry Component
215
-
216
- The `<Retry>` component configures retry behavior:
217
-
218
- ```tsx
219
- <Retry
220
- maxAttempts={3}
221
- feedbackOnError={true}
222
- retryOnToolError={true}
223
- />
224
- ```
225
-
226
- ## 🎯 Examples
227
-
228
- ### Simple Summarizer
229
-
230
- ```tsx
231
- const Summarizer = (
232
- <Agent id="summarizer" input={InputSchema} output={OutputSchema}>
233
- <Prompt>
234
- <System>You are a summarization expert.</System>
235
- <User>{(ctx: any): string => `Summarize: ${ctx.inputs.text}`}</User>
236
- </Prompt>
237
- <Result>
238
- {(ctx: any, llmResult: any) => ({
239
- summary: llmResult.response.content
240
- })}
241
- </Result>
242
- <Validate>
243
- {(output: any) => ({
244
- rules: [
245
- {
246
- check: output.summary.length >= 50,
247
- error: 'Summary too short'
248
- }
249
- ]
250
- })}
251
- </Validate>
252
- <Retry maxAttempts={2} feedbackOnError={true} />
253
- </Agent>
254
- );
255
- ```
256
-
257
- ### Code Analyzer with Tools
258
-
259
- ```tsx
260
- import { createMemoryTools } from '@limo-labs/deity';
261
-
262
- const CodeAnalyzer = (
263
- <Agent
264
- id="analyzer"
265
- input={z.object({ repoPath: z.string() })}
266
- output={z.object({ findings: z.array(z.any()) })}
267
- tools={[
268
- ...createMemoryTools(),
269
- {
270
- name: 'read_file',
271
- description: 'Read file contents',
272
- inputSchema: z.object({ path: z.string() }),
273
- async execute(input) {
274
- return { content: await fs.readFile(input.path, 'utf-8') };
275
- }
276
- }
277
- ]}
278
- >
279
- <Prompt>
280
- <System>
281
- {async (): Promise<string> => {
282
- return await fs.readFile('./prompts/analyzer.md', 'utf-8');
283
- }}
284
- </System>
285
- <User>
286
- {(ctx: any): string => `Analyze repository at: ${ctx.inputs.repoPath}`}
287
- </User>
288
- </Prompt>
289
- <Result>
290
- {(ctx: any, llmResult: any) => {
291
- // Count memory_store tool calls to verify analysis
292
- const findings = llmResult.response.toolCalls
293
- ?.filter((tc: any) => tc.name === 'memory_store')
294
- .map((tc: any) => JSON.parse(tc.arguments));
295
-
296
- return { findings };
297
- }}
298
- </Result>
299
- </Agent>
300
- );
301
- ```
302
-
303
- ### Dynamic Tool Factory (NEW in 4.0)
304
-
305
- **Tool Factory Pattern** - Create tools dynamically based on execution context:
306
-
307
- ```tsx
308
- import { createMemoryTools } from '@limo-labs/deity';
309
-
310
- const DynamicAgent = (
311
- <Agent
312
- id="dynamic"
313
- input={z.object({ enableMemory: z.boolean() })}
314
- output={z.object({ result: z.string() })}
315
- tools={(ctx) => {
316
- // Static tools always available
317
- const staticTools = [
318
- {
319
- name: 'read_file',
320
- description: 'Read file',
321
- inputSchema: z.object({ path: z.string() }),
322
- async execute(input) {
323
- return await fs.readFile(input.path, 'utf-8');
324
- }
325
- }
326
- ];
327
-
328
- // Add memory tools conditionally
329
- const memoryTools = ctx.memory ? createMemoryTools(ctx) : [];
330
-
331
- return [...staticTools, ...memoryTools];
332
- }}
333
- >
334
- {/* ... */}
335
- </Agent>
336
- );
337
- ```
338
-
339
- **Key Benefits:**
340
- - ✅ Tools can access ExecutionContext at creation time
341
- - ✅ Conditional tool inclusion based on context state
342
- - ✅ Enables Deity's memory tools in TSX workflows
343
- - ✅ Backward compatible with static tool arrays
344
-
345
- ### Multi-Step Workflow
346
-
347
- ```tsx
348
- const MultiStepAgent = (
349
- <Agent id="multi-step" input={InputSchema} output={OutputSchema}>
350
- <Prompt>
351
- <System>
352
- {async (): Promise<string> => {
353
- const step = ctx.inputs.currentStep;
354
- return await loadStepPrompt(step);
355
- }}
356
- </System>
357
- <User>{(ctx: any): string => buildStepMessage(ctx)}</User>
358
- </Prompt>
359
- <Observe>
360
- {(llmResult: any) => ({
361
- toolCallCount: llmResult.response.toolCalls?.length || 0,
362
- roundsExecuted: llmResult.rounds
363
- })}
364
- </Observe>
365
- <Result>
366
- {(ctx: any, llmResult: any, observed: any) => {
367
- console.log(`Executed ${observed.roundsExecuted} rounds`);
368
- return extractStepOutput(llmResult);
369
- }}
370
- </Result>
371
- </Agent>
372
- );
373
- ```
374
-
375
- ## 🔧 Advanced Features
376
-
377
- ### Loop Validation
378
-
379
- Use `loopValidator` to validate during LLM execution loops:
380
-
381
- ```typescript
382
- import type { Validator, LLMLoopState, ExecutionContext } from '@limo-labs/deity';
383
-
384
- class CustomValidator implements Validator {
385
- async validate(
386
- ctx: ExecutionContext,
387
- loopState: LLMLoopState
388
- ): Promise<ValidationResult> {
389
- // Check if required tool was called
390
- const requiredTool = loopState.toolCallsThisRound.find(
391
- tc => tc.name === 'submit_plan'
392
- );
393
-
394
- if (!requiredTool) {
395
- return {
396
- valid: false,
397
- feedback: 'You must call submit_plan tool to complete this task'
398
- };
399
- }
400
-
401
- return { valid: true };
402
- }
403
- }
404
-
405
- const agent = (
406
- <Agent
407
- id="planner"
408
- input={InputSchema}
409
- output={OutputSchema}
410
- loopValidator={new CustomValidator()}
411
- >
412
- {/* ... */}
413
- </Agent>
414
- );
415
- ```
416
-
417
- ### Loop Configuration
418
-
419
- Configure LLM execution loop behavior:
420
-
421
- ```tsx
422
- <Agent
423
- id="agent"
424
- input={InputSchema}
425
- output={OutputSchema}
426
- loopConfig={{
427
- maxToolRounds: 15, // Max tool execution rounds
428
- timeout: 180000 // 3 minute timeout
429
- }}
430
- >
431
- {/* ... */}
432
- </Agent>
433
- ```
434
-
435
- ### Dynamic Prompts
436
-
437
- Load prompts from files or construct dynamically:
438
-
439
- ```tsx
440
- <System>
441
- {async (ctx: any): Promise<string> => {
442
- // Load from file
443
- const template = await fs.readFile('./prompts/system.md', 'utf-8');
444
-
445
- // Inject dynamic content
446
- return template.replace('{{language}}', ctx.inputs.language);
447
- }}
448
- </System>
449
- ```
450
-
451
- ## 🎨 Component Composition
452
-
453
- Agents can be composed from reusable functions:
454
-
455
- ```typescript
456
- // Reusable prompt builders
457
- function buildSystemPrompt(role: string) {
458
- return async (): Promise<string> => {
459
- return `You are a ${role}.`;
460
- };
461
- }
462
-
463
- function buildUserPrompt(template: string) {
464
- return (ctx: any): string => {
465
- return template.replace(/\{\{(\w+)\}\}/g, (_, key) => ctx.inputs[key]);
466
- };
467
- }
468
-
469
- // Reusable result extractors
470
- function extractJSONFromToolCall(toolName: string) {
471
- return (ctx: any, llmResult: any) => {
472
- const toolCall = llmResult.response.toolCalls?.find(
473
- (tc: any) => tc.name === toolName
474
- );
475
- return JSON.parse(toolCall.arguments);
476
- };
477
- }
478
-
479
- // Compose agent
480
- const agent = (
481
- <Agent id="composer" input={InputSchema} output={OutputSchema}>
482
- <Prompt>
483
- <System>{buildSystemPrompt('expert')}</System>
484
- <User>{buildUserPrompt('Process {{task}}')}</User>
485
- </Prompt>
486
- <Result>{extractJSONFromToolCall('submit')}</Result>
487
- </Agent>
488
- );
489
- ```
490
-
491
- ## 📚 Utilities
492
-
493
- ### Tool Result Extraction
494
-
495
- ```typescript
496
- import { extractLastToolResult, extractAllToolResults, countToolCalls } from '@limo-labs/deity';
497
-
498
- // Extract single tool result
499
- const result = extractLastToolResult(llmResult, 'tool_name', {
500
- unwrap: true,
501
- required: true
502
- });
503
-
504
- // Extract all results from a tool
505
- const allResults = extractAllToolResults(llmResult, 'memory_store');
506
-
507
- // Count tool calls
508
- const count = countToolCalls(llmResult, 'memory_store', (result) => {
509
- return result?.success === true;
510
- });
511
- ```
512
-
513
- ### Memory Tools
514
-
515
- ```typescript
516
- import { createMemoryTools } from '@limo-labs/deity';
517
-
518
- const agent = (
519
- <Agent
520
- id="agent"
521
- input={InputSchema}
522
- output={OutputSchema}
523
- tools={createMemoryTools()}
524
- >
525
- {/* Agent can now use memory_store, memory_recall, etc. */}
526
- </Agent>
527
- );
528
- ```
529
-
530
- ## 🏗️ Architecture
531
-
532
- ```
533
- ┌─────────────────────────────────────────┐
534
- │ JSX/TSX Components │
535
- │ <Agent>, <Prompt>, <Result>, etc. │
536
- └─────────────────────────────────────────┘
537
- ↓ compileAgent()
538
- ┌─────────────────────────────────────────┐
539
- │ AST Nodes │
540
- │ AgentNode, PromptNode, ResultNode │
541
- └─────────────────────────────────────────┘
542
- ↓ executeComponent()
543
- ┌─────────────────────────────────────────┐
544
- │ Execution Engine │
545
- │ - Build Prompt │
546
- │ - Call LLM Loop │
547
- │ - Extract Output │
548
- │ - Validate │
549
- │ - Retry if needed │
550
- └─────────────────────────────────────────┘
551
- ```
552
-
553
- ## 🧪 Testing
554
-
555
- Deity includes utilities for testing agents:
556
-
557
- ```typescript
558
- import { testFullAgent } from '@limo-labs/deity';
559
-
560
- const result = await testFullAgent(agent, {
561
- inputs: { task: 'test' },
562
- mockLLMResponse: {
563
- content: '{"result": "success"}',
564
- toolCalls: []
565
- }
566
- });
567
-
568
- expect(result.output).toEqual({ result: 'success' });
569
- ```
570
-
571
- ## 📖 Documentation
572
-
573
- - [API Reference](./docs/API.md) - Full API documentation
574
- - [**Declarative Workflow Guide**](./docs/TSX_WORKFLOW.md) - **NEW: Build workflows with declarative API**
575
- - [Workflow Quick Reference](./docs/TSX_WORKFLOW_QUICK_REF.md) - Quick syntax reference
576
- - [Migration Guide](./docs/MIGRATION.md) - Migrating from imperative API
577
- - [Best Practices](./docs/BEST_PRACTICES.md) - Design patterns and tips
578
- - [Examples](./docs/EXAMPLES.md) - Real-world examples
579
-
580
- ## 🔀 Declarative Workflows (NEW in 4.0)
581
-
582
- Deity 4.0 introduces **Declarative Workflow API** - build complex multi-agent workflows with function composition:
583
-
584
- **Note:** Despite the name "TSX Workflow", these use **function calls** (`.ts` files), not JSX tags. Only Agent definitions support true JSX/TSX syntax.
585
-
586
- ```typescript
587
- import { Workflow, Sequence, Parallel, Conditional, runTSXWorkflow } from '@limo-labs/deity';
588
-
589
- const workflow = Workflow({
590
- name: 'code-analysis',
591
- defaultModel: { adapter: llmAdapter },
592
- children: Sequence({
593
- children: [
594
- // Step 1: Classify code
595
- ClassifierAgent,
596
-
597
- // Step 2: Branch based on complexity
598
- Conditional({
599
- condition: (ctx) => ctx.getOutput('classifier').isComplex,
600
- children: [
601
- // Complex: parallel deep analysis
602
- Parallel({
603
- children: [
604
- SecurityAnalyzer,
605
- PerformanceAnalyzer,
606
- QualityAnalyzer,
607
- ],
608
- }),
609
- // Simple: quick summary
610
- QuickSummary,
611
- ],
612
- }),
613
-
614
- // Step 3: Generate report
615
- ReportGenerator,
616
- ],
617
- }),
618
- });
619
-
620
- // Execute
621
- const result = await runTSXWorkflow(workflow, { code: '...' });
622
- ```
623
-
624
- ### Workflow Components
625
-
626
- - **`Sequence`** - Execute children in order (function call)
627
- - **`Parallel`** - Execute children concurrently (function call)
628
- - **`Conditional`** - Branch based on condition (function call)
629
- - **`Loop`** - Execute child N times (function call)
630
- - **`ForEach`** - Execute child for each item in array (function call)
631
-
632
- **[➡️ Full TSX Workflow Guide](./docs/TSX_WORKFLOW.md)**
633
-
634
- ## 🔗 Integration
635
-
636
- ### LLM Adapters
637
-
638
- Deity works with any LLM adapter that implements the `LLMAdapter` interface:
639
-
640
- ```typescript
641
- interface LLMAdapter {
642
- generate(
643
- messages: Message[],
644
- tools?: Tool[],
645
- config?: GenerationConfig,
646
- ctx?: ExecutionContext
647
- ): Promise<LLMResponse>;
648
- }
649
- ```
650
-
651
- Example adapters:
652
- - `@limo-labs/deity-adapter-openai` - OpenAI GPT models
653
- - `@limo-labs/deity-adapter-anthropic` - Claude models
654
- - `@limo-labs/deity-adapter-copilot` - GitHub Copilot SDK
655
-
656
- ### Framework Integration
657
-
658
- - **Express/Fastify** - Use as API handlers
659
- - **Next.js** - Server actions and API routes
660
- - **Electron** - Desktop app agents
661
- - **CLI Tools** - Command-line AI agents
662
-
663
- ## 🤝 Contributing
664
-
665
- Contributions are welcome! Please see [CONTRIBUTING.md](./CONTRIBUTING.md).
666
-
667
- ## 📄 License
668
-
669
- MIT © [Limo Labs](https://github.com/limo-labs)
670
-
671
- ## 🔗 Links
672
-
673
- - [GitHub](https://github.com/Limo-Labs/Limo-Deity)
674
- - [npm](https://www.npmjs.com/package/@limo-labs/deity)
675
- - [Documentation](https://deity.limo.run)
676
- - [Issues](https://github.com/Limo-Labs/Limo-Deity/issues)
677
- - [Changelog](./CHANGELOG.md)
678
-
679
- ---
680
-
681
- **Built with ❤️ by Limo Labs**