@koi-language/koi 1.0.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/QUICKSTART.md +89 -0
- package/README.md +545 -0
- package/examples/actions-demo.koi +177 -0
- package/examples/cache-test.koi +29 -0
- package/examples/calculator.koi +61 -0
- package/examples/clear-registry.js +33 -0
- package/examples/clear-registry.koi +30 -0
- package/examples/code-introspection-test.koi +149 -0
- package/examples/counter.koi +132 -0
- package/examples/delegation-test.koi +52 -0
- package/examples/directory-import-test.koi +84 -0
- package/examples/hello-world-claude.koi +52 -0
- package/examples/hello-world.koi +52 -0
- package/examples/hello.koi +24 -0
- package/examples/mcp-example.koi +70 -0
- package/examples/multi-event-handler-test.koi +144 -0
- package/examples/new-import-test.koi +89 -0
- package/examples/pipeline.koi +162 -0
- package/examples/registry-demo.koi +184 -0
- package/examples/registry-playbook-demo.koi +162 -0
- package/examples/registry-playbook-email-compositor-2.koi +140 -0
- package/examples/registry-playbook-email-compositor.koi +140 -0
- package/examples/sentiment.koi +90 -0
- package/examples/simple.koi +48 -0
- package/examples/skill-import-test.koi +76 -0
- package/examples/skills/advanced/index.koi +95 -0
- package/examples/skills/math-operations.koi +69 -0
- package/examples/skills/string-operations.koi +56 -0
- package/examples/task-chaining-demo.koi +244 -0
- package/examples/test-await.koi +22 -0
- package/examples/test-crypto-sha256.koi +196 -0
- package/examples/test-delegation.koi +41 -0
- package/examples/test-multi-team-routing.koi +258 -0
- package/examples/test-no-handler.koi +35 -0
- package/examples/test-npm-import.koi +67 -0
- package/examples/test-parse.koi +10 -0
- package/examples/test-peers-with-team.koi +59 -0
- package/examples/test-permissions-fail.koi +20 -0
- package/examples/test-permissions.koi +36 -0
- package/examples/test-simple-registry.koi +31 -0
- package/examples/test-typescript-import.koi +64 -0
- package/examples/test-uses-team-syntax.koi +25 -0
- package/examples/test-uses-team.koi +31 -0
- package/examples/utils/calculator.test.ts +144 -0
- package/examples/utils/calculator.ts +56 -0
- package/examples/utils/math-helpers.js +50 -0
- package/examples/utils/math-helpers.ts +55 -0
- package/examples/web-delegation-demo.koi +165 -0
- package/package.json +78 -0
- package/src/cli/koi.js +793 -0
- package/src/compiler/build-optimizer.js +447 -0
- package/src/compiler/cache-manager.js +274 -0
- package/src/compiler/import-resolver.js +369 -0
- package/src/compiler/parser.js +7542 -0
- package/src/compiler/transpiler.js +1105 -0
- package/src/compiler/typescript-transpiler.js +148 -0
- package/src/grammar/koi.pegjs +767 -0
- package/src/runtime/action-registry.js +172 -0
- package/src/runtime/actions/call-skill.js +45 -0
- package/src/runtime/actions/format.js +115 -0
- package/src/runtime/actions/print.js +42 -0
- package/src/runtime/actions/registry-delete.js +37 -0
- package/src/runtime/actions/registry-get.js +37 -0
- package/src/runtime/actions/registry-keys.js +33 -0
- package/src/runtime/actions/registry-search.js +34 -0
- package/src/runtime/actions/registry-set.js +50 -0
- package/src/runtime/actions/return.js +31 -0
- package/src/runtime/actions/send-message.js +58 -0
- package/src/runtime/actions/update-state.js +36 -0
- package/src/runtime/agent.js +1368 -0
- package/src/runtime/cli-logger.js +205 -0
- package/src/runtime/incremental-json-parser.js +201 -0
- package/src/runtime/index.js +33 -0
- package/src/runtime/llm-provider.js +1372 -0
- package/src/runtime/mcp-client.js +1171 -0
- package/src/runtime/planner.js +273 -0
- package/src/runtime/registry-backends/keyv-sqlite.js +215 -0
- package/src/runtime/registry-backends/local.js +260 -0
- package/src/runtime/registry.js +162 -0
- package/src/runtime/role.js +14 -0
- package/src/runtime/router.js +395 -0
- package/src/runtime/runtime.js +113 -0
- package/src/runtime/skill-selector.js +173 -0
- package/src/runtime/skill.js +25 -0
- package/src/runtime/team.js +162 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Koi — Sentiment Analysis Skill Demo
|
|
3
|
+
// Skill with agents using playbooks ONLY (LLM execution)
|
|
4
|
+
// ============================================================
|
|
5
|
+
|
|
6
|
+
package "demo.koi.sentiment"
|
|
7
|
+
|
|
8
|
+
role Worker { can execute, can propose }
|
|
9
|
+
role Reviewer { can critique, can approve }
|
|
10
|
+
|
|
11
|
+
Skill SentimentAnalysis {
|
|
12
|
+
affordance """
|
|
13
|
+
Analyzes text sentiment and returns positive/neutral/negative classification.
|
|
14
|
+
Uses 2 agents: Analyst (drafts analysis) + Reviewer (validates and normalizes).
|
|
15
|
+
Both agents use playbooks executed by LLM.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
// Agent with ONLY playbook (LLM executes this)
|
|
19
|
+
Agent Analyst : Worker {
|
|
20
|
+
llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.3, max_tokens: 300 }
|
|
21
|
+
|
|
22
|
+
on analyze(args: Json) {
|
|
23
|
+
playbook """
|
|
24
|
+
You are a sentiment analysis expert. Analyze the sentiment of the text provided in args.text.
|
|
25
|
+
|
|
26
|
+
Your task:
|
|
27
|
+
1. Determine if the sentiment is positive, neutral, or negative
|
|
28
|
+
2. Assign a confidence score between 0 and 1
|
|
29
|
+
3. Provide a brief rationale (max 100 characters)
|
|
30
|
+
|
|
31
|
+
Return ONLY this exact JSON structure (no markdown, no explanations):
|
|
32
|
+
{
|
|
33
|
+
"label": "positive|neutral|negative",
|
|
34
|
+
"score": 0.85,
|
|
35
|
+
"rationale": "Brief explanation here"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
Rules:
|
|
39
|
+
- Base your analysis ONLY on the provided text
|
|
40
|
+
- If ambiguous, use "neutral" with score ~0.5
|
|
41
|
+
- Keep rationale under 100 characters
|
|
42
|
+
"""
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Agent with ONLY playbook (LLM executes this)
|
|
47
|
+
Agent Reviewer : Reviewer {
|
|
48
|
+
llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.0, max_tokens: 200 }
|
|
49
|
+
|
|
50
|
+
on normalize(args: Json) {
|
|
51
|
+
playbook """
|
|
52
|
+
You are a quality reviewer. Validate and normalize the sentiment analysis draft in args.draft.
|
|
53
|
+
|
|
54
|
+
Your task:
|
|
55
|
+
1. Ensure label is exactly "positive", "neutral", or "negative"
|
|
56
|
+
2. Ensure score is a number between 0 and 1
|
|
57
|
+
3. Ensure rationale is under 100 characters
|
|
58
|
+
4. Fix any issues while preserving the original intent
|
|
59
|
+
|
|
60
|
+
Return ONLY this exact JSON structure (no markdown, no explanations):
|
|
61
|
+
{
|
|
62
|
+
"label": "positive|neutral|negative",
|
|
63
|
+
"score": 0.85,
|
|
64
|
+
"rationale": "Brief explanation here"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
If the draft is missing fields, fill them conservatively (neutral, score 0.5).
|
|
68
|
+
"""
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
Team Internal {
|
|
73
|
+
analyst = Analyst
|
|
74
|
+
reviewer = Reviewer
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function run(input: any): Promise<any> {
|
|
78
|
+
const text = "I absolutely love this product! It works perfectly and exceeded all my expectations."
|
|
79
|
+
|
|
80
|
+
const draft =
|
|
81
|
+
await send Internal.event("analyze").role(Worker).any()({ text: text }) timeout 15s
|
|
82
|
+
|
|
83
|
+
const final =
|
|
84
|
+
await send Internal.event("normalize").role(Reviewer).any()({ draft: draft }) timeout 10s
|
|
85
|
+
|
|
86
|
+
return final
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
run SentimentAnalysis.run({})
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Koi — Simple Example
|
|
3
|
+
// Minimal example showing agent communication with procedural code
|
|
4
|
+
// ============================================================
|
|
5
|
+
|
|
6
|
+
package "demo.koi.simple"
|
|
7
|
+
|
|
8
|
+
role Worker { can execute }
|
|
9
|
+
role Lead { can delegate }
|
|
10
|
+
|
|
11
|
+
// Agent with procedural code only (no playbook)
|
|
12
|
+
Agent Greeter : Worker {
|
|
13
|
+
on greet(args: Json) {
|
|
14
|
+
const name = args.name
|
|
15
|
+
return { message: "Hello, " + name + "!" }
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Agent with procedural code only (no playbook)
|
|
20
|
+
Agent Processor : Worker {
|
|
21
|
+
on process(args: Json) {
|
|
22
|
+
const msg = args.message
|
|
23
|
+
const upper = "PROCESSED: " + msg
|
|
24
|
+
return { result: upper }
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
Team SimpleTeam {
|
|
29
|
+
greeter = Greeter
|
|
30
|
+
processor = Processor
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Orchestrator with procedural code
|
|
34
|
+
Agent Orchestrator : Lead {
|
|
35
|
+
uses Team SimpleTeam
|
|
36
|
+
|
|
37
|
+
on start(args: Json) {
|
|
38
|
+
const greeting =
|
|
39
|
+
await send peers.event("greet").role(Worker).any()({ name: "Koi" }) timeout 5s
|
|
40
|
+
|
|
41
|
+
const processed =
|
|
42
|
+
await send peers.event("process").role(Worker).any()({ message: greeting.message }) timeout 5s
|
|
43
|
+
|
|
44
|
+
return processed
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
run Orchestrator.start({})
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Skill Import Test
|
|
3
|
+
// Tests importing Skills from external files
|
|
4
|
+
// ============================================================
|
|
5
|
+
|
|
6
|
+
package "demo.koi.import"
|
|
7
|
+
|
|
8
|
+
// Import the Math Operations skill
|
|
9
|
+
import "./skills/math-operations.koi"
|
|
10
|
+
|
|
11
|
+
role Calculator { can calculate }
|
|
12
|
+
|
|
13
|
+
// Agent that uses the imported MathOperations skill
|
|
14
|
+
Agent MathAgent : Calculator {
|
|
15
|
+
llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.1, max_tokens: 200 }
|
|
16
|
+
uses Skill MathOperations
|
|
17
|
+
|
|
18
|
+
on calculate(args: Json) {
|
|
19
|
+
playbook """
|
|
20
|
+
Perform the following calculation: ${JSON.stringify(args)}
|
|
21
|
+
|
|
22
|
+
Use the available math tools to compute the result.
|
|
23
|
+
Return the result as JSON with the operation and result fields.
|
|
24
|
+
"""
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Create a team
|
|
29
|
+
Team MathTeam {
|
|
30
|
+
calculator = MathAgent
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Demo runner
|
|
34
|
+
Agent DemoRunner : Calculator {
|
|
35
|
+
uses Team MathTeam
|
|
36
|
+
|
|
37
|
+
on runTest(args: Json) {
|
|
38
|
+
console.log("╔════════════════════════════════════════════╗")
|
|
39
|
+
console.log("║ Skill Import Test ║")
|
|
40
|
+
console.log("║ Testing imported Math Operations ║")
|
|
41
|
+
console.log("╚════════════════════════════════════════════╝\n")
|
|
42
|
+
|
|
43
|
+
// Test 1: Addition
|
|
44
|
+
console.log("📋 Test 1: Addition (5 + 3)")
|
|
45
|
+
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
|
46
|
+
|
|
47
|
+
const result1 =
|
|
48
|
+
await send peers.event("calculate").role(Calculator).any()({
|
|
49
|
+
operation: "add",
|
|
50
|
+
a: 5,
|
|
51
|
+
b: 3
|
|
52
|
+
}) timeout 10s
|
|
53
|
+
|
|
54
|
+
console.log("Result:", JSON.stringify(result1, null, 2))
|
|
55
|
+
|
|
56
|
+
// Test 2: Multiplication
|
|
57
|
+
console.log("\n📋 Test 2: Multiplication (7 × 6)")
|
|
58
|
+
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
|
59
|
+
|
|
60
|
+
const result2 =
|
|
61
|
+
await send peers.event("calculate").role(Calculator).any()({
|
|
62
|
+
operation: "multiply",
|
|
63
|
+
a: 7,
|
|
64
|
+
b: 6
|
|
65
|
+
}) timeout 10s
|
|
66
|
+
|
|
67
|
+
console.log("Result:", JSON.stringify(result2, null, 2))
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
success: true,
|
|
71
|
+
tests_completed: 2
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
run DemoRunner.runTest({})
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Advanced Skills Package
|
|
3
|
+
// Entry point that exports all skills from this directory
|
|
4
|
+
// ============================================================
|
|
5
|
+
|
|
6
|
+
package "skills.advanced"
|
|
7
|
+
|
|
8
|
+
// Re-export from other files in this directory
|
|
9
|
+
// (In a future version, we could support automatic re-export)
|
|
10
|
+
|
|
11
|
+
// For now, we define everything here as the main entry point
|
|
12
|
+
|
|
13
|
+
role DataScientist { can analyze }
|
|
14
|
+
|
|
15
|
+
// ============================================================
|
|
16
|
+
// Statistical Analysis Skill
|
|
17
|
+
// ============================================================
|
|
18
|
+
|
|
19
|
+
Skill StatisticalAnalysis {
|
|
20
|
+
affordance """
|
|
21
|
+
Perform statistical analysis including mean, median, standard deviation
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
export async function calculateMean(args: any): Promise<any> {
|
|
25
|
+
const numbers = args.numbers || [];
|
|
26
|
+
if (numbers.length === 0) {
|
|
27
|
+
return { error: "No numbers provided" };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const sum = numbers.reduce((a, b) => a + b, 0);
|
|
31
|
+
const mean = sum / numbers.length;
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
operation: "mean",
|
|
35
|
+
numbers: numbers,
|
|
36
|
+
result: mean
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function calculateMedian(args: any): Promise<any> {
|
|
41
|
+
const numbers = args.numbers || [];
|
|
42
|
+
if (numbers.length === 0) {
|
|
43
|
+
return { error: "No numbers provided" };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const sorted = [...numbers].sort((a, b) => a - b);
|
|
47
|
+
const mid = Math.floor(sorted.length / 2);
|
|
48
|
+
const median = sorted.length % 2 === 0
|
|
49
|
+
? (sorted[mid - 1] + sorted[mid]) / 2
|
|
50
|
+
: sorted[mid];
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
operation: "median",
|
|
54
|
+
numbers: numbers,
|
|
55
|
+
result: median
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function calculateStdDev(args: any): Promise<any> {
|
|
60
|
+
const numbers = args.numbers || [];
|
|
61
|
+
if (numbers.length === 0) {
|
|
62
|
+
return { error: "No numbers provided" };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const mean = numbers.reduce((a, b) => a + b, 0) / numbers.length;
|
|
66
|
+
const variance = numbers.reduce((sum, num) => sum + Math.pow(num - mean, 2), 0) / numbers.length;
|
|
67
|
+
const stdDev = Math.sqrt(variance);
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
operation: "stddev",
|
|
71
|
+
numbers: numbers,
|
|
72
|
+
mean: mean,
|
|
73
|
+
variance: variance,
|
|
74
|
+
result: stdDev
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ============================================================
|
|
80
|
+
// Data Analysis Agent
|
|
81
|
+
// ============================================================
|
|
82
|
+
|
|
83
|
+
Agent DataAnalyzer : DataScientist {
|
|
84
|
+
llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.1, max_tokens: 400 }
|
|
85
|
+
uses Skill StatisticalAnalysis
|
|
86
|
+
|
|
87
|
+
on analyze(args: Json) {
|
|
88
|
+
playbook """
|
|
89
|
+
Analyze this data: ${JSON.stringify(args)}
|
|
90
|
+
|
|
91
|
+
Use the available statistical tools to compute mean, median, and standard deviation.
|
|
92
|
+
Return a comprehensive analysis as JSON.
|
|
93
|
+
"""
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Math Operations Skill
|
|
3
|
+
// A reusable skill that provides math operations
|
|
4
|
+
// ============================================================
|
|
5
|
+
|
|
6
|
+
package "skills.math"
|
|
7
|
+
|
|
8
|
+
Skill MathOperations {
|
|
9
|
+
affordance """
|
|
10
|
+
Perform mathematical operations like addition, subtraction, multiplication, and division
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
export async function add(args: any): Promise<any> {
|
|
14
|
+
const a = args.a || 0;
|
|
15
|
+
const b = args.b || 0;
|
|
16
|
+
console.log(`[add] ${a} + ${b} = ${a + b}`);
|
|
17
|
+
return {
|
|
18
|
+
operation: "addition",
|
|
19
|
+
a: a,
|
|
20
|
+
b: b,
|
|
21
|
+
result: a + b
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function subtract(args: any): Promise<any> {
|
|
26
|
+
const a = args.a || 0;
|
|
27
|
+
const b = args.b || 0;
|
|
28
|
+
console.log(`[subtract] ${a} - ${b} = ${a - b}`);
|
|
29
|
+
return {
|
|
30
|
+
operation: "subtraction",
|
|
31
|
+
a: a,
|
|
32
|
+
b: b,
|
|
33
|
+
result: a - b
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function multiply(args: any): Promise<any> {
|
|
38
|
+
const a = args.a || 0;
|
|
39
|
+
const b = args.b || 0;
|
|
40
|
+
console.log(`[multiply] ${a} × ${b} = ${a * b}`);
|
|
41
|
+
return {
|
|
42
|
+
operation: "multiplication",
|
|
43
|
+
a: a,
|
|
44
|
+
b: b,
|
|
45
|
+
result: a * b
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function divide(args: any): Promise<any> {
|
|
50
|
+
const a = args.a || 0;
|
|
51
|
+
const b = args.b || 1;
|
|
52
|
+
if (b === 0) {
|
|
53
|
+
console.error(`[divide] Error: Division by zero`);
|
|
54
|
+
return {
|
|
55
|
+
operation: "division",
|
|
56
|
+
a: a,
|
|
57
|
+
b: b,
|
|
58
|
+
error: "Division by zero"
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
console.log(`[divide] ${a} ÷ ${b} = ${a / b}`);
|
|
62
|
+
return {
|
|
63
|
+
operation: "division",
|
|
64
|
+
a: a,
|
|
65
|
+
b: b,
|
|
66
|
+
result: a / b
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// String Operations Skill
|
|
3
|
+
// A reusable skill for string manipulation
|
|
4
|
+
// ============================================================
|
|
5
|
+
|
|
6
|
+
package "skills.string"
|
|
7
|
+
|
|
8
|
+
Skill StringOperations {
|
|
9
|
+
affordance """
|
|
10
|
+
Perform string operations like uppercase, lowercase, reverse, and length
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
export async function toUpperCase(args: any): Promise<any> {
|
|
14
|
+
const text = args.text || "";
|
|
15
|
+
const result = text.toUpperCase();
|
|
16
|
+
console.log(`[toUpperCase] "${text}" → "${result}"`);
|
|
17
|
+
return {
|
|
18
|
+
operation: "toUpperCase",
|
|
19
|
+
original: text,
|
|
20
|
+
result: result
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function toLowerCase(args: any): Promise<any> {
|
|
25
|
+
const text = args.text || "";
|
|
26
|
+
const result = text.toLowerCase();
|
|
27
|
+
console.log(`[toLowerCase] "${text}" → "${result}"`);
|
|
28
|
+
return {
|
|
29
|
+
operation: "toLowerCase",
|
|
30
|
+
original: text,
|
|
31
|
+
result: result
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function reverse(args: any): Promise<any> {
|
|
36
|
+
const text = args.text || "";
|
|
37
|
+
const result = text.split('').reverse().join('');
|
|
38
|
+
console.log(`[reverse] "${text}" → "${result}"`);
|
|
39
|
+
return {
|
|
40
|
+
operation: "reverse",
|
|
41
|
+
original: text,
|
|
42
|
+
result: result
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function getLength(args: any): Promise<any> {
|
|
47
|
+
const text = args.text || "";
|
|
48
|
+
const length = text.length;
|
|
49
|
+
console.log(`[getLength] "${text}" has ${length} characters`);
|
|
50
|
+
return {
|
|
51
|
+
operation: "length",
|
|
52
|
+
text: text,
|
|
53
|
+
length: length
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Task Chaining Demo
|
|
3
|
+
// Demonstrates automatic output→input chaining between tasks
|
|
4
|
+
// ============================================================
|
|
5
|
+
|
|
6
|
+
package "demo.koi.chaining"
|
|
7
|
+
|
|
8
|
+
role Worker { can execute }
|
|
9
|
+
role Assistant { can help }
|
|
10
|
+
|
|
11
|
+
// ============================================================
|
|
12
|
+
// Specialized Worker Agents
|
|
13
|
+
// ============================================================
|
|
14
|
+
|
|
15
|
+
Agent FrenchTranslator : Worker {
|
|
16
|
+
llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.2, max_tokens: 200 }
|
|
17
|
+
|
|
18
|
+
on translate(args: Json) {
|
|
19
|
+
playbook """
|
|
20
|
+
You are a specialized worker agent. Do NOT return actions, only direct results.
|
|
21
|
+
|
|
22
|
+
Translate the following text to French: ${JSON.stringify(args.text)}
|
|
23
|
+
|
|
24
|
+
Return ONLY valid JSON (NO actions field):
|
|
25
|
+
{
|
|
26
|
+
"translated": "text in French",
|
|
27
|
+
"original": "original text",
|
|
28
|
+
"language": "french"
|
|
29
|
+
}
|
|
30
|
+
"""
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
Agent WordCounter : Worker {
|
|
35
|
+
llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.1, max_tokens: 200 }
|
|
36
|
+
|
|
37
|
+
on count(args: Json) {
|
|
38
|
+
playbook """
|
|
39
|
+
You are a specialized worker agent. Do NOT return actions, only direct results.
|
|
40
|
+
|
|
41
|
+
Count the number of words in this text: ${JSON.stringify(args.text)}
|
|
42
|
+
|
|
43
|
+
Return ONLY valid JSON (NO actions field):
|
|
44
|
+
{
|
|
45
|
+
"wordCount": <number>,
|
|
46
|
+
"text": "the text that was counted"
|
|
47
|
+
}
|
|
48
|
+
"""
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
Agent TextFormatter : Worker {
|
|
53
|
+
llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.1, max_tokens: 200 }
|
|
54
|
+
|
|
55
|
+
on format(args: Json) {
|
|
56
|
+
playbook """
|
|
57
|
+
You are a specialized worker agent. Do NOT return actions, only direct results.
|
|
58
|
+
|
|
59
|
+
Convert this text to uppercase: ${JSON.stringify(args.text)}
|
|
60
|
+
|
|
61
|
+
Return ONLY valid JSON (NO actions field):
|
|
62
|
+
{
|
|
63
|
+
"formatted": "TEXT IN UPPERCASE",
|
|
64
|
+
"original": "original text"
|
|
65
|
+
}
|
|
66
|
+
"""
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
Agent CharacterCounter : Worker {
|
|
71
|
+
llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.1, max_tokens: 200 }
|
|
72
|
+
|
|
73
|
+
on countChars(args: Json) {
|
|
74
|
+
playbook """
|
|
75
|
+
You are a specialized worker agent. Do NOT return actions, only direct results.
|
|
76
|
+
|
|
77
|
+
Count the number of characters in this text: ${JSON.stringify(args.text)}
|
|
78
|
+
|
|
79
|
+
Return ONLY valid JSON (NO actions field):
|
|
80
|
+
{
|
|
81
|
+
"charCount": <number>,
|
|
82
|
+
"text": "the text that was counted"
|
|
83
|
+
}
|
|
84
|
+
"""
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
Agent DataValidator : Worker {
|
|
89
|
+
llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.1, max_tokens: 200 }
|
|
90
|
+
|
|
91
|
+
on validate(args: Json) {
|
|
92
|
+
playbook """
|
|
93
|
+
You are a specialized worker agent. Do NOT return actions, only direct results.
|
|
94
|
+
|
|
95
|
+
Validate this email: ${JSON.stringify(args.email)}
|
|
96
|
+
|
|
97
|
+
Check if it's a valid email format.
|
|
98
|
+
|
|
99
|
+
Return ONLY valid JSON (NO actions field):
|
|
100
|
+
{
|
|
101
|
+
"valid": true/false,
|
|
102
|
+
"email": "the email",
|
|
103
|
+
"message": "validation message"
|
|
104
|
+
}
|
|
105
|
+
"""
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ============================================================
|
|
110
|
+
// Regular Assistant - NO special orchestrator knowledge
|
|
111
|
+
// ============================================================
|
|
112
|
+
|
|
113
|
+
Agent TaskAssistant : Assistant {
|
|
114
|
+
llm default = { provider: "openai", model: "gpt-4o-mini", temperature: 0.3, max_tokens: 800 }
|
|
115
|
+
|
|
116
|
+
on helpUser(args: Json) {
|
|
117
|
+
playbook """
|
|
118
|
+
User request: ${args.request}
|
|
119
|
+
|
|
120
|
+
Help accomplish this request. If it requires multiple steps:
|
|
121
|
+
1. Break it down into sequential tasks
|
|
122
|
+
2. Chain outputs: use previous results as inputs for next tasks
|
|
123
|
+
|
|
124
|
+
The system will automatically:
|
|
125
|
+
- Execute each task
|
|
126
|
+
- Pass outputs to next task
|
|
127
|
+
- Route to appropriate team members
|
|
128
|
+
|
|
129
|
+
Return your plan or result as JSON.
|
|
130
|
+
"""
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ============================================================
|
|
135
|
+
// Team
|
|
136
|
+
// ============================================================
|
|
137
|
+
|
|
138
|
+
Team AssistantTeam {
|
|
139
|
+
taskAssistant = TaskAssistant
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ============================================================
|
|
143
|
+
// Demo Runner
|
|
144
|
+
// ============================================================
|
|
145
|
+
|
|
146
|
+
Agent DemoRunner : Assistant {
|
|
147
|
+
uses Team AssistantTeam
|
|
148
|
+
|
|
149
|
+
on runDemo(args: Json) {
|
|
150
|
+
console.log("╔════════════════════════════════════════════╗")
|
|
151
|
+
console.log("║ Task Chaining Demo ║")
|
|
152
|
+
console.log("║ Output → Input Automatic Chaining ║")
|
|
153
|
+
console.log("╚════════════════════════════════════════════╝\n")
|
|
154
|
+
|
|
155
|
+
// ============================================================
|
|
156
|
+
// Test 1: Translate → Count Words
|
|
157
|
+
// ============================================================
|
|
158
|
+
console.log("📋 Test 1: Translate to French → Count Words")
|
|
159
|
+
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
|
160
|
+
console.log('Request: "Translate Hello world to French and then count the words"\n')
|
|
161
|
+
|
|
162
|
+
const test1 = await send peers.event("helpUser").role(Assistant).any()({
|
|
163
|
+
request: "Translate 'Hello world' to French and then count the words in the translation"
|
|
164
|
+
}) timeout 30s
|
|
165
|
+
|
|
166
|
+
console.log("✅ Result:")
|
|
167
|
+
console.log(JSON.stringify(test1, null, 2))
|
|
168
|
+
console.log("\nExpected flow:")
|
|
169
|
+
console.log(" 1. FrenchTranslator → { translated: 'Bonjour monde' }")
|
|
170
|
+
console.log(" 2. WordCounter(previousResult.translated) → { wordCount: 2 }")
|
|
171
|
+
console.log("\n")
|
|
172
|
+
|
|
173
|
+
// ============================================================
|
|
174
|
+
// Test 2: Format → Count Characters
|
|
175
|
+
// ============================================================
|
|
176
|
+
console.log("📋 Test 2: Format to Uppercase → Count Characters")
|
|
177
|
+
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
|
178
|
+
console.log('Request: "Format hello to uppercase and count characters"\n')
|
|
179
|
+
|
|
180
|
+
const test2 = await send peers.event("helpUser").role(Assistant).any()({
|
|
181
|
+
request: "Format the word hello to uppercase and then count the characters"
|
|
182
|
+
}) timeout 30s
|
|
183
|
+
|
|
184
|
+
console.log("✅ Result:")
|
|
185
|
+
console.log(JSON.stringify(test2, null, 2))
|
|
186
|
+
console.log("\nExpected flow:")
|
|
187
|
+
console.log(" 1. TextFormatter → { formatted: 'HELLO' }")
|
|
188
|
+
console.log(" 2. CharacterCounter(previousResult.formatted) → { charCount: 5 }")
|
|
189
|
+
console.log("\n")
|
|
190
|
+
|
|
191
|
+
// ============================================================
|
|
192
|
+
// Test 3: Chain of 3 Tasks
|
|
193
|
+
// ============================================================
|
|
194
|
+
console.log("📋 Test 3: Three-Step Chain")
|
|
195
|
+
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
|
196
|
+
console.log('Request: "Validate email, format it, count words"\n')
|
|
197
|
+
|
|
198
|
+
const test3 = await send peers.event("helpUser").role(Assistant).any()({
|
|
199
|
+
request: "Validate the email user@test.com, then format it to uppercase, then count how many words it has"
|
|
200
|
+
}) timeout 30s
|
|
201
|
+
|
|
202
|
+
console.log("✅ Result:")
|
|
203
|
+
console.log(JSON.stringify(test3, null, 2))
|
|
204
|
+
console.log("\nExpected flow:")
|
|
205
|
+
console.log(" 1. DataValidator → { valid: true, email: 'user@test.com' }")
|
|
206
|
+
console.log(" 2. TextFormatter(previousResult.email) → { formatted: 'USER@TEST.COM' }")
|
|
207
|
+
console.log(" 3. WordCounter(previousResult.formatted) → { wordCount: 1 }")
|
|
208
|
+
console.log("\n")
|
|
209
|
+
|
|
210
|
+
// ============================================================
|
|
211
|
+
// Test 4: Complex Original Request
|
|
212
|
+
// ============================================================
|
|
213
|
+
console.log("📋 Test 4: Your Original Example")
|
|
214
|
+
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
|
|
215
|
+
console.log('Request: "Traduce al frances y luego cuenta el numero de palabras"\n')
|
|
216
|
+
|
|
217
|
+
const test4 = await send peers.event("helpUser").role(Assistant).any()({
|
|
218
|
+
request: "Traduce 'artificial intelligence' al frances y luego cuenta el numero de palabras"
|
|
219
|
+
}) timeout 30s
|
|
220
|
+
|
|
221
|
+
console.log("✅ Result:")
|
|
222
|
+
console.log(JSON.stringify(test4, null, 2))
|
|
223
|
+
console.log("\nExpected flow:")
|
|
224
|
+
console.log(" 1. FrenchTranslator → { translated: 'intelligence artificielle' }")
|
|
225
|
+
console.log(" 2. WordCounter(previousResult.translated) → { wordCount: 2 }")
|
|
226
|
+
console.log("\n")
|
|
227
|
+
|
|
228
|
+
console.log("✅ All chaining tests completed!")
|
|
229
|
+
console.log("")
|
|
230
|
+
console.log("🔑 Key Points:")
|
|
231
|
+
console.log(" - LLM creates plan with chained actions")
|
|
232
|
+
console.log(" - Each action references previous results: $-{previousResult.field}")
|
|
233
|
+
console.log(" - Runtime resolves references before executing")
|
|
234
|
+
console.log(" - Output of action N becomes input of action N+1")
|
|
235
|
+
console.log(" - NO manual wiring needed!")
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
success: true,
|
|
239
|
+
tests_completed: 4
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
run DemoRunner.runDemo({})
|