Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/javascript/dialogue-evaluator/.env.example
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
OPENAI_API_KEY=xxxx
OPENAI_BASE_URL=xxxx
OPENAI_MODEL=xxxx
102 changes: 102 additions & 0 deletions examples/javascript/dialogue-evaluator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Dialogue Evaluator

This example demonstrates how to build a multi-task workflow in Capsule that processes data, interacts with an external LLM (Language Model), and manages file I/O.

## Setup

You need to install packages:
```bash
npm install -g @capsule-run/cli
npm install
```

And create a `.env` file in this directory by copying the example:
```bash
cp .env.example .env
```

Edit the `.env` file and add your API details:
```env
OPENAI_API_KEY=your_api_key_here
OPENAI_BASE_URL=https://api.openai.com/v1 # or your custom endpoint
OPENAI_MODEL=gpt-4o-mini # or your preferred model
```

## Running the Application

Execute the workflow using the Capsule CLI:
```bash
capsule run --verbose
```

Or run the specific entrypoint:
```bash
capsule run main.ts --verbose
```

## Detailed Workflow Logic

```Mermaid
---
config:
theme: mc
themeVariables:
fontSize: 16px
look: neo
layout: dagre
---
flowchart LR
subgraph input["INPUT"]
A["Main Task<br>---<br>Compute: HIGH"]
B["Get Dialogue Lines<br>---<br>Compute: LOW"]
C["Read JSON<br>source/dialogue-lines.json"]
end
subgraph loop["PROCESSING LOOP"]
D{"For each<br>dialogue line"}
E["Agent Task<br>---<br>Timeout: 1.8s<br>Retries: 1"]
end
subgraph evaluation["EVALUATION"]
G["Evaluate Dialogue Line<br>---<br>Call OpenAI API"]
H{"API<br>Response"}
end
subgraph output["OUTPUT"]
I["Insert Dialogue Line<br>---<br>Append to CSV"]
J["Write<br>output/evaluated-dialogue-lines.csv"]
end
subgraph control["CONTROL"]
K{"More<br>lines?"}
L["Report Statistics<br>---<br>Success / Failed counts"]
end
A --> B
B --> C
C --> D
D -- Yes --> E
G --> H
H -- Success --> I
I --> J
J --> K
H -. Timeout/Error .-> M["Failed"]
M -.-> K
K -- Yes --> D
K -- No --> L
L --> N["End"]

style A fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
style B fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
style E fill:#fff3e0,stroke:#f57c00,stroke-width:2px
style G fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
style I fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
style L fill:#e0f2f1,stroke:#00796b,stroke-width:2px
style M fill:#ffebee,stroke:#c62828,stroke-width:2px
```

## Output

After running, check the results in `output/evaluated-dialogue-lines.csv`:
```csv
Line,Emotion
"What's in the suitcase?",neutral
"You're out of your mind.",angry
"I'll buy it off you.",happy
```

39 changes: 14 additions & 25 deletions examples/javascript/dialogue-evaluator/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { task, files, env } from "@capsule-run/sdk";
import OpenAI from "openai";

const getDialogueLines = task({
name: "getDialogueLines",
name: "Get Dialogue Lines",
compute: "LOW",
allowedFiles: ["source"]
}, async (): Promise<{lines: string[]}> => {
Expand All @@ -11,7 +11,7 @@ const getDialogueLines = task({
})

const insertDialogueLine = task({
name: "insertDialogueLine",
name: "Save Dialogue Line",
allowedFiles: ["output"]
}, async (line: string, evaluation: string): Promise<void> => {
const output = await files.readText('output/evaluated-dialogue-lines.csv')
Expand All @@ -23,28 +23,28 @@ const insertDialogueLine = task({
})

const evaluateDialogueLine = task({
name: "evaluateDialogueLine",
envVariables: ["OPENAI_API_KEY"]
name: "Evaluate Dialogue Line",
envVariables: ["OPENAI_API_KEY", "OPENAI_BASE_URL", "OPENAI_MODEL"]
}, async (prompt: string): Promise<string> => {

const openai = new OpenAI({
baseURL: 'YOUR BASE URL',
baseURL: env.get('OPENAI_BASE_URL'),
apiKey: env.get('OPENAI_API_KEY'),
});

const completion = await openai.chat.completions.create({
messages: [{ role: "user", content: prompt }],
model: "YOUR MODEL",
model: env.get('OPENAI_MODEL'),
});

return completion.choices[0].message.content || "";
})

const agent = task({
name: "agent",
compute: "HIGH",
maxRetries: 2,
envVariables: ["OPENAI_API_KEY"]
name: "Agent Evaluator",
compute: "MEDIUM",
maxRetries: 1,
timeout: "1.8s"
}, async (line: string) => {
const prompt = `
Analyze the emotional tone of the following dialogue line: "${line}"
Expand All @@ -63,41 +63,30 @@ const agent = task({
return;
})

// entrypoint
task({name: "main", compute: "HIGH"}, async () => {

const getDialogueLinesResponse = await getDialogueLines();
const lines = getDialogueLinesResponse.result.lines;

const failedAttempts = [];
let failedAttempts = 0;
let successfulAttempts = 0;

for (const [index, line] of lines.entries()) {
console.log(`\n💬 [${index + 1}/${lines.length}] Analyzing: "${line}"`);
const agentResponse = await agent(line);

if(!agentResponse.success) {
if(failedAttempts.length == 3) {
break;
}

failedAttempts.push(line);
failedAttempts++;
continue;
}

successfulAttempts++;

if(successfulAttempts == 3) {
break;
}

console.log(`Agent-${index} successfully evaluated line`);
}

console.log(
"\n ----------\n",
`Agent created: ${lines.length}\n`,
`Successful attempts: ${successfulAttempts}\n`,
`Failed attempts: ${failedAttempts.length}\n`,
`Failed attempts: ${failedAttempts}\n`,
"----------\n"
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
text,evaluation
dialogue,evaluation
37 changes: 0 additions & 37 deletions examples/javascript/dialogue-evaluator/package-lock.json

This file was deleted.

37 changes: 3 additions & 34 deletions examples/javascript/dialogue-evaluator/source/dialogue-lines.json
Original file line number Diff line number Diff line change
@@ -1,55 +1,24 @@
{
"lines": [
"Open sesame.",
"What's in the suitcase?",
"Don't mind me.",
"Kinda hard when you're hoverin' over us, eh? Don't you got somewhere to be?",
"What, th-this piece of junk? Yeah, psh, prolly nothin'.",
"Mhm. Cans... uh, maybe bottles... heh, who knows?",
"I'll buy it off you.",
"How did you get it?",
"You from the lost and found or somethin'? 'Ey, if it isn't your biz, then don't go makin' it.",
"Hm... now lemme think...",
"You're out of your mind.",
"Don't like it, don't buy it.",
"you won't get it open anyway.",
"We'll see about that. Where'd you find it?",
"I said, where?!",
"Th-those dumpsters, over there!",
"For fuck's sake, Josh...",
"That suitcase is more trouble than it's worth.",
"It wasn't stolen. We found it.",
"Look, it wasn't us, all right? Gonk was already cold! We swear!",
"Wait, really? And here I had you pegged for top-tier mercs... Now, outta my way.",
"Wait, really? And here I had you pegged for top-tier mercs... Now get outta my way.",
"Ugh! Forget it...",
"Ugh, forget it...",
"Open sesame.",
"How much to take it off your hands?",
"Stuff like that doesn't just get lost. Won't find anything in there, 'cept trouble...",
"How'd you find that? Doesn't take an expert to see it's corp-owned.",
"What's in the briefcase?",
"Say what? You need to get your heads checked.",
"Say what? You need to go get your heads checked.",
"I'm done talking with you.",
"You know what? I'm done talking to you both.",
"Don't let us keep ya.",
"You just had to open your mouth, didn't ya?",
"Gimme a fuckin' break already!",
"I don't have that much.",
"Short right now, but I'll be come up with something.",
"I'm short right now, but I'll be come up with somethin'.",
"Fine. Here you go.",
"Well, look what we have here...",
"Well, I'll be damned...",
"Hoooly... it opened! We're fuckin' rich!",
"Dumpsters... same place the shard was...",
"Huh... and where are you meant to go...?",
"Nope, used mine for smokes. Wherever you wanna be, sure as shit ain't here!",
"Give it to me. Now.",
"Hey, hey, take it easy, boss... W-we can work something out, right? Come on...",
"Don't make me repeat myself.",
"Yeah, whatever you say. Just know that whoever comes lookin'... won't be askin' questions.",
"Whatever you say. Just know that whoever comes lookin'... won't be askin' questions.",
"What're you waitin' for? Give it to 'er!"
"Hoooly... it opened! We're rich!",
"Huh... and where are you meant to go...?"
]
}