Skip to content
This repository was archived by the owner on Nov 26, 2025. It is now read-only.

Commit c87562c

Browse files
Merge pull request #3 from rohas-dev/feat/support-tools-in-llm
feat: implement flow statement enhancements with output handling and flow registration
2 parents 08e7c84 + 4d979f5 commit c87562c

9 files changed

Lines changed: 250 additions & 10 deletions

File tree

.github/workflows/build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ jobs:
6363
~/.cargo/registry/cache/
6464
~/.cargo/git/db/
6565
target/
66-
key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock') }}
66+
key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('Cargo.lock') }}
6767
restore-keys: |
6868
${{ runner.os }}-cargo-${{ matrix.target }}-
6969

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ jobs:
6666
~/.cargo/registry/cache/
6767
~/.cargo/git/db/
6868
target/
69-
key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock') }}
69+
key: ${{ runner.os }}-cargo-${{ matrix.target }}-${{ hashFiles('Cargo.lock') }}
7070
restore-keys: |
7171
${{ runner.os }}-cargo-${{ matrix.target }}-
7272

examples/flow-news.ro

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
flow fetchNews {
2+
step search {
3+
prompt "Get top 5 tech news today"
4+
output { headlines: string[] }
5+
}
6+
}
7+
8+
flow summarizeNews {
9+
uses fetchNews
10+
step summarize {
11+
prompt "Summarize these headlines in 3 sentences"
12+
output { summary: string }
13+
}
14+
}

rohas-cli/src/main.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rohas_core::{Lexer, Parser};
44
use rohas_flow::FlowEngine;
55
use rohas_llm::{LLMProvider, ProviderConfig};
66
use rohas_optimizer::TokenSaver;
7-
use rohas_runtime::{Executor, Value};
7+
use rohas_runtime::Executor;
88
use std::fs;
99
use std::path::PathBuf;
1010

@@ -113,6 +113,11 @@ fn main() -> anyhow::Result<()> {
113113
}
114114

115115
let flow_engine = FlowEngine::new(executor);
116+
for statement in &program.statements {
117+
if let rohas_core::ast::Statement::FlowStatement { name, .. } = statement {
118+
flow_engine.register_flow(name.clone(), statement.clone());
119+
}
120+
}
116121

117122
let mut executor_guard = flow_engine.executor.lock().unwrap();
118123
let mut results = Vec::new();

rohas-core/src/ast.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ pub enum Statement {
8383

8484
FlowStatement {
8585
name: String,
86+
uses: Vec<String>,
8687
steps: Vec<FlowStep>,
8788
},
8889
StepStatement {
@@ -115,6 +116,7 @@ pub struct FlowStep {
115116
pub parallel: bool,
116117
pub condition: Option<Expression>,
117118
pub retry: Option<RetryConfig>,
119+
pub output: Option<Expression>,
118120
}
119121

120122
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]

rohas-core/src/lexer.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ impl Lexer {
2525
keywords.insert("async".to_string(), Token::Async);
2626
keywords.insert("await".to_string(), Token::Await);
2727
keywords.insert("use".to_string(), Token::Use);
28+
keywords.insert("uses".to_string(), Token::Use);
2829
keywords.insert("import".to_string(), Token::Import);
2930
keywords.insert("export".to_string(), Token::Export);
3031
keywords.insert("type".to_string(), Token::Type);
@@ -42,6 +43,7 @@ impl Lexer {
4243
keywords.insert("state".to_string(), Token::State);
4344
keywords.insert("print".to_string(), Token::Print);
4445
keywords.insert("input".to_string(), Token::Input);
46+
keywords.insert("output".to_string(), Token::Output);
4547

4648
keywords.insert("flow".to_string(), Token::Flow);
4749
keywords.insert("step".to_string(), Token::Step);

rohas-core/src/parser.rs

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ impl Parser {
6868
Some(Token::ToolCall) => self.parse_tool_call_statement(),
6969
Some(Token::Call) => self.parse_call_statement(),
7070
Some(Token::Print) => self.parse_print_statement(),
71+
Some(Token::Output) => self.parse_output_statement(),
7172
Some(Token::Identifier(_)) => {
7273

7374
let start_pos = self.current;
@@ -254,7 +255,7 @@ impl Parser {
254255
fn parse_use_statement(&mut self) -> Result<Statement, ParseError> {
255256
self.consume(Token::Use)?;
256257
self.skip_whitespace();
257-
let name = self.consume_identifier()?;
258+
let _name = self.consume_identifier()?;
258259
self.skip_whitespace();
259260
self.consume_semicolon_optional();
260261

@@ -351,6 +352,53 @@ impl Parser {
351352
Ok(Statement::PrintStatement { expression })
352353
}
353354

355+
fn parse_output_statement(&mut self) -> Result<Statement, ParseError> {
356+
self.consume(Token::Output)?;
357+
self.skip_whitespace();
358+
self.consume(Token::LeftBrace)?;
359+
self.skip_whitespace();
360+
let mut properties = HashMap::new();
361+
while !self.check(Token::RightBrace) {
362+
let key = self.consume_identifier()?;
363+
self.skip_whitespace();
364+
self.consume(Token::Colon)?;
365+
self.skip_whitespace();
366+
let value = if let Some(Token::Identifier(_)) = self.peek() {
367+
let start_pos = self.current;
368+
let type_name = self.consume_identifier()?;
369+
let is_array = self.match_token(Token::LeftBracket);
370+
if is_array {
371+
self.consume(Token::RightBracket)?;
372+
if self.check(Token::LeftParen) || self.check(Token::Dot) || self.check(Token::LeftBracket) {
373+
self.current = start_pos;
374+
self.parse_expression()?
375+
} else {
376+
Expression::Literal(Literal::String(format!("{}[]", type_name)))
377+
}
378+
} else {
379+
if self.check(Token::LeftParen) || self.check(Token::Dot) || self.check(Token::LeftBracket) {
380+
self.current = start_pos;
381+
self.parse_expression()?
382+
} else {
383+
Expression::Literal(Literal::String(type_name))
384+
}
385+
}
386+
} else {
387+
self.parse_expression()?
388+
};
389+
properties.insert(key, value);
390+
self.skip_whitespace();
391+
if !self.check(Token::RightBrace) {
392+
self.match_token(Token::Comma);
393+
self.skip_whitespace();
394+
}
395+
}
396+
self.consume(Token::RightBrace)?;
397+
Ok(Statement::ExpressionStatement {
398+
expression: Expression::ObjectLiteral { properties },
399+
})
400+
}
401+
354402
fn parse_parallel_step_statement(&mut self) -> Result<Statement, ParseError> {
355403

356404
self.consume(Token::Parallel)?;
@@ -688,8 +736,30 @@ impl Parser {
688736
self.consume(Token::Flow)?;
689737
let name = self.consume_identifier()?;
690738
self.consume(Token::LeftBrace)?;
739+
let mut uses = Vec::new();
691740
let mut steps = Vec::new();
692741

742+
while !self.check(Token::RightBrace) {
743+
self.skip_whitespace();
744+
if self.check(Token::RightBrace) {
745+
break;
746+
}
747+
748+
if self.match_token(Token::Use) {
749+
let flow_name = self.consume_identifier()?;
750+
uses.push(flow_name);
751+
self.skip_whitespace();
752+
self.consume_semicolon_optional();
753+
continue;
754+
}
755+
756+
if self.check(Token::Step) || self.check(Token::Parallel) {
757+
break;
758+
}
759+
760+
break;
761+
}
762+
693763
while !self.check(Token::RightBrace) {
694764
self.skip_whitespace();
695765
if self.check(Token::RightBrace) {
@@ -738,19 +808,38 @@ impl Parser {
738808
None
739809
};
740810
self.consume(Token::LeftBrace)?;
741-
let statements = self.parse_block()?;
811+
let mut statements = Vec::new();
812+
let mut output = None;
813+
814+
while !self.check(Token::RightBrace) && !self.is_at_end() {
815+
self.skip_whitespace();
816+
if self.check(Token::RightBrace) {
817+
break;
818+
}
819+
820+
if self.check(Token::Output) {
821+
let output_stmt = self.parse_output_statement()?;
822+
if let Statement::ExpressionStatement { expression } = output_stmt {
823+
output = Some(expression);
824+
}
825+
} else {
826+
statements.push(self.parse_statement()?);
827+
}
828+
}
829+
742830
self.consume(Token::RightBrace)?;
743831
steps.push(FlowStep {
744832
name: step_name,
745833
statements,
746834
parallel,
747835
condition,
748836
retry,
837+
output,
749838
});
750839
}
751840

752841
self.consume(Token::RightBrace)?;
753-
Ok(Statement::FlowStatement { name, steps })
842+
Ok(Statement::FlowStatement { name, uses, steps })
754843
}
755844

756845
fn parse_step_statement(&mut self) -> Result<Statement, ParseError> {

rohas-core/src/token.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ pub enum Token {
3838
State,
3939
Print,
4040
Input,
41+
Output,
4142

4243
Flow,
4344
Step,
@@ -126,6 +127,7 @@ impl Token {
126127
| Token::State
127128
| Token::Print
128129
| Token::Input
130+
| Token::Output
129131
| Token::Flow
130132
| Token::Step
131133
| Token::Parallel

0 commit comments

Comments
 (0)