Skip to content

Commit 5a54cb9

Browse files
authored
feat(LLMCall): BREAKING CHANGE Add support for LLM Executor (#18)
Include fixes for HTTP Executor Updated tests and snapshot schema Updated schema.json Updated documentation **Type System Unification:** - Replace separate INT and FLOAT types with unified NUM type - Update ValueType and InputType enums in schema.py - Implement smart num parsing: returns int for whole numbers, float for decimals - Migrate all 74 workflow files from type: int/float to type: num - Update type coercion logic in executors_core.py - Regenerate schema.json with new type definitions **Bug Fix:** - Fix composition-output-passing workflow bug - Replace bash arithmetic $((...)) with Python for proper float handling - Child calculator now correctly processes float operations - Verified: 10 * 5 = 50, then 50 + 5 = 55 (was 50 before) **Updates:** - Update README.md and CLAUDE.md documentation - Regenerate 68 test snapshots with correct type handling - Add 4 new workflow output type test cases - All 34/35 workflows passing (1 paused as expected) **BREAKING CHANGE:** - Workflow YAML files using `type: int` or `type: float` should migrate to `type: num`
2 parents 2cf0b66 + 1677f60 commit 5a54cb9

122 files changed

Lines changed: 3392 additions & 6661 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ __marimo__/
211211
.hooks/
212212
.mcp.json
213213
.scratch/
214-
local/
214+
.workflows/
215215
ARCHITECTURE.md
216216
CLAUDE.md
217217
docs/

README.md

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ Restart your LLM client, and you're ready to go!
7070

7171
- **DAG-Based Workflows**: Automatic dependency resolution and parallel execution
7272
- **🔐 Secrets Management**: Server-side credential handling with automatic redaction (v5.0.0+)
73+
- **🤖 LLM Integration**: Native LLM API calls with schema validation and retry logic
7374
- **Workflow Composition**: Reusable workflows via Workflow blocks
7475
- **Conditional Execution**: Boolean expressions for dynamic control flow
7576
- **Variable Resolution**: Five-namespace system (inputs, metadata, blocks, secrets, __internal__)
@@ -114,6 +115,47 @@ blocks:
114115
}
115116
```
116117

118+
### 🤖 LLM Integration
119+
120+
Call LLM APIs directly from workflows with automatic retry logic and JSON schema validation:
121+
122+
```yaml
123+
blocks:
124+
- id: extract_data
125+
type: LLMCall
126+
inputs:
127+
provider: openai
128+
model: gpt-4o-mini
129+
api_key: "{{secrets.OPENAI_API_KEY}}"
130+
prompt: "Extract key information from: {{inputs.text}}"
131+
response_schema:
132+
type: object
133+
required: [summary, confidence]
134+
properties:
135+
summary: {type: string}
136+
confidence: {type: number, minimum: 0, maximum: 1}
137+
max_retries: 3
138+
```
139+
140+
**Supported Providers:**
141+
142+
- **OpenAI** - API access with native Structured Outputs
143+
- **Anthropic** - API access for Claude models
144+
- **Gemini** - API access for Gemini models
145+
- **Ollama** - Local Ollama server API
146+
- **OpenAI-compatible** - LM Studio, vLLM, and other compatible endpoints
147+
148+
**Key Features:**
149+
150+
- ✅ **Native schema validation** - Send JSON Schema to API for guaranteed structure
151+
- ✅ **Automatic retry** - Exponential backoff with validation feedback loop
152+
- ✅ **Token tracking** - Monitor usage and costs
153+
- ✅ **Secrets integration** - Secure API key management
154+
155+
**For Local CLI Tools:**
156+
157+
To use local Claude CLI or Gemini CLI instead of APIs, use the `llm-process` workflow template which wraps CLI commands.
158+
117159
## What Can You Do With It?
118160

119161
### Built-in Workflows
@@ -184,9 +226,33 @@ blocks:
184226
command: printf "Hello, {{inputs.name}}!"
185227
186228
outputs:
187-
greeting: "{{blocks.greet.outputs.stdout}}"
229+
greeting:
230+
value: "{{blocks.greet.outputs.stdout}}"
231+
type: str
232+
description: "The greeting message"
188233
```
189234

235+
### Workflow Outputs
236+
237+
Workflow outputs support automatic type coercion, allowing you to declare the expected type and get properly typed values:
238+
239+
```yaml
240+
outputs:
241+
output_name:
242+
value: "{{blocks.block_id.outputs.field}}" # Variable expression
243+
type: str # Type declaration (optional)
244+
description: "Human-readable description" # Documentation (optional)
245+
```
246+
247+
**Supported Types:**
248+
249+
- **str** - Text values (default if type not specified)
250+
- **num** - Numeric values (integer or float - JSON doesn't distinguish)
251+
- **bool** - Boolean values (true/false)
252+
- **list** - List/array values
253+
- **dict** - Dictionary/object values
254+
- **json** - Parse JSON string into dict/list/str/num/bool/None
255+
190256
### Key Features
191257

192258
**Variable Substitution:**

generate_schema.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#!/usr/bin/env python3
2+
"""Generate schema.json from Pydantic models.
3+
4+
This script regenerates the workflow schema JSON file that provides:
5+
- VS Code YAML autocomplete
6+
- Workflow validation
7+
- Documentation generation
8+
9+
Usage:
10+
python generate_schema.py
11+
"""
12+
13+
import json
14+
from pathlib import Path
15+
16+
from workflows_mcp.engine.executor_base import create_default_registry
17+
18+
19+
def main():
20+
"""Generate and save schema.json."""
21+
print("Generating workflow schema from executor registry...")
22+
23+
# Create registry with all built-in executors
24+
registry = create_default_registry()
25+
26+
# Generate complete schema
27+
schema = registry.generate_workflow_schema()
28+
29+
# Save to schema.json in project root
30+
schema_path = Path(__file__).parent / "schema.json"
31+
32+
with open(schema_path, "w") as f:
33+
json.dump(schema, f, indent=2)
34+
f.write("\n") # Add trailing newline
35+
36+
print(f"✓ Schema generated: {schema_path}")
37+
print(f" Executors: {len([k for k in schema.get('$defs', {}).keys() if k.endswith('Input')])}")
38+
print(f" Schema size: {schema_path.stat().st_size:,} bytes")
39+
40+
41+
if __name__ == "__main__":
42+
main()

0 commit comments

Comments
 (0)