Skip to content
Closed
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
13 changes: 7 additions & 6 deletions core/examples/manual_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@
"""

import asyncio
import logging
from framework.graph import Goal, NodeSpec, EdgeSpec, GraphSpec, EdgeCondition

from framework.graph import EdgeCondition, EdgeSpec, Goal, GraphSpec, NodeSpec
from framework.graph.executor import GraphExecutor
from framework.runtime.core import Runtime


# 1. Define Node Logic (Pure Python Functions)
def greet(name: str) -> str:
"""Generate a simple greeting."""
Expand All @@ -38,7 +39,7 @@ async def main():
description="Generate a friendly uppercase greeting",
success_criteria=[
{
"id": "greeting_generated",
"id": "greeting_generated",
"description": "Greeting produced",
"metric": "custom",
"target": "any"
Expand All @@ -63,7 +64,7 @@ async def main():
name="Uppercaser",
description="Converts greeting to uppercase",
node_type="function",
function="uppercase",
function="uppercase",
input_keys=["greeting"],
output_keys=["final_greeting"]
)
Expand Down Expand Up @@ -100,8 +101,8 @@ async def main():
executor.register_function("uppercaser", uppercase)

# 8. Execute Agent
print(f"▶ Executing agent with input: name='Alice'...")
print("▶ Executing agent with input: name='Alice'...")

result = await executor.execute(
graph=graph,
goal=goal,
Expand Down
18 changes: 9 additions & 9 deletions core/framework/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,22 @@
See `framework.testing` for details.
"""

from framework.schemas.decision import Decision, Option, Outcome, DecisionEvaluation
from framework.schemas.run import Run, RunSummary, Problem
from framework.runtime.core import Runtime
from framework.builder.query import BuilderQuery
from framework.llm import LLMProvider, AnthropicProvider
from framework.runner import AgentRunner, AgentOrchestrator
from framework.llm import AnthropicProvider, LLMProvider
from framework.runner import AgentOrchestrator, AgentRunner
from framework.runtime.core import Runtime
from framework.schemas.decision import Decision, DecisionEvaluation, Option, Outcome
from framework.schemas.run import Problem, Run, RunSummary

# Testing framework
from framework.testing import (
ApprovalStatus,
DebugTool,
ErrorCategory,
Test,
TestResult,
TestSuiteResult,
TestStorage,
ApprovalStatus,
ErrorCategory,
DebugTool,
TestSuiteResult,
)

__all__ = [
Expand Down
6 changes: 3 additions & 3 deletions core/framework/builder/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

from framework.builder.query import BuilderQuery
from framework.builder.workflow import (
GraphBuilder,
BuildSession,
BuildPhase,
ValidationResult,
BuildSession,
GraphBuilder,
TestCase,
TestResult,
ValidationResult,
)

__all__ = [
Expand Down
6 changes: 3 additions & 3 deletions core/framework/builder/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
4. What should we change? (suggestions)
"""

from typing import Any
from collections import defaultdict
from pathlib import Path
from typing import Any

from framework.schemas.decision import Decision
from framework.schemas.run import Run, RunSummary, RunStatus
from framework.schemas.run import Run, RunStatus, RunSummary
from framework.storage.backend import FileStorage


Expand Down Expand Up @@ -476,7 +476,7 @@ def _find_differences(self, run1: Run, run2: Run) -> list[str]:
)

# Find first divergence point
for i, (d1, d2) in enumerate(zip(run1.decisions, run2.decisions)):
for i, (d1, d2) in enumerate(zip(run1.decisions, run2.decisions, strict=False)):
if d1.chosen_option_id != d2.chosen_option_id:
differences.append(
f"Diverged at decision {i}: chose '{d1.chosen_option_id}' vs '{d2.chosen_option_id}'"
Expand Down
69 changes: 35 additions & 34 deletions core/framework/builder/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,17 @@
You cannot skip steps or bypass validation.
"""

from collections.abc import Callable
from datetime import datetime
from enum import Enum
from pathlib import Path
from datetime import datetime
from typing import Any, Callable
from typing import Any

from pydantic import BaseModel, Field

from framework.graph.edge import EdgeCondition, EdgeSpec, GraphSpec
from framework.graph.goal import Goal
from framework.graph.node import NodeSpec
from framework.graph.edge import EdgeSpec, EdgeCondition, GraphSpec


class BuildPhase(str, Enum):
Expand Down Expand Up @@ -630,69 +631,69 @@ def _generate_code(self, graph: GraphSpec) -> str:
"""Generate Python code for the graph."""
lines = [
'"""',
f'Generated agent: {self.session.name}',
f'Generated at: {datetime.now().isoformat()}',
f"Generated agent: {self.session.name}",
f"Generated at: {datetime.now().isoformat()}",
'"""',
'',
'from framework.graph import (',
' Goal, SuccessCriterion, Constraint,',
' NodeSpec, EdgeSpec, EdgeCondition,',
')',
'from framework.graph.edge import GraphSpec',
'from framework.graph.goal import GoalStatus',
'',
'',
'# Goal',
"",
"from framework.graph import (",
" Goal, SuccessCriterion, Constraint,",
" NodeSpec, EdgeSpec, EdgeCondition,",
")",
"from framework.graph.edge import GraphSpec",
"from framework.graph.goal import GoalStatus",
"",
"",
"# Goal",
]

if self.session.goal:
goal_json = self.session.goal.model_dump_json(indent=4)
lines.append('GOAL = Goal.model_validate_json(\'\'\'')
lines.append("GOAL = Goal.model_validate_json('''")
lines.append(goal_json)
lines.append("''')")
else:
lines.append('GOAL = None')
lines.append("GOAL = None")

lines.extend([
'',
'',
'# Nodes',
'NODES = [',
"",
"",
"# Nodes",
"NODES = [",
])

for node in self.session.nodes:
node_json = node.model_dump_json(indent=4)
lines.append(' NodeSpec.model_validate_json(\'\'\'')
lines.append(" NodeSpec.model_validate_json('''")
lines.append(node_json)
lines.append(" '''),")

lines.extend([
']',
'',
'',
'# Edges',
'EDGES = [',
"]",
"",
"",
"# Edges",
"EDGES = [",
])

for edge in self.session.edges:
edge_json = edge.model_dump_json(indent=4)
lines.append(' EdgeSpec.model_validate_json(\'\'\'')
lines.append(" EdgeSpec.model_validate_json('''")
lines.append(edge_json)
lines.append(" '''),")

lines.extend([
']',
'',
'',
'# Graph',
"]",
"",
"",
"# Graph",
])

graph_json = graph.model_dump_json(indent=4)
lines.append('GRAPH = GraphSpec.model_validate_json(\'\'\'')
lines.append("GRAPH = GraphSpec.model_validate_json('''")
lines.append(graph_json)
lines.append("''')")

return '\n'.join(lines)
return "\n".join(lines)

# =========================================================================
# SESSION MANAGEMENT
Expand Down
32 changes: 16 additions & 16 deletions core/framework/graph/__init__.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
"""Graph structures: Goals, Nodes, Edges, and Flexible Execution."""

from framework.graph.goal import Goal, SuccessCriterion, Constraint, GoalStatus
from framework.graph.node import NodeSpec, NodeContext, NodeResult, NodeProtocol
from framework.graph.edge import EdgeSpec, EdgeCondition, GraphSpec
from framework.graph.code_sandbox import CodeSandbox, safe_eval, safe_exec
from framework.graph.edge import EdgeCondition, EdgeSpec, GraphSpec
from framework.graph.executor import GraphExecutor
from framework.graph.flexible_executor import ExecutorConfig, FlexibleGraphExecutor
from framework.graph.goal import Constraint, Goal, GoalStatus, SuccessCriterion
from framework.graph.judge import HybridJudge, create_default_judge
from framework.graph.node import NodeContext, NodeProtocol, NodeResult, NodeSpec

# Flexible execution (Worker-Judge pattern)
from framework.graph.plan import (
Plan,
PlanStep,
ActionSpec,
ActionType,
StepStatus,
Judgment,
JudgmentAction,
EvaluationRule,
PlanExecutionResult,
ExecutionStatus,
load_export,
# HITL (Human-in-the-loop)
ApprovalDecision,
ApprovalRequest,
ApprovalResult,
EvaluationRule,
ExecutionStatus,
Judgment,
JudgmentAction,
Plan,
PlanExecutionResult,
PlanStep,
StepStatus,
load_export,
)
from framework.graph.judge import HybridJudge, create_default_judge
from framework.graph.worker_node import WorkerNode, StepExecutionResult
from framework.graph.flexible_executor import FlexibleGraphExecutor, ExecutorConfig
from framework.graph.code_sandbox import CodeSandbox, safe_exec, safe_eval
from framework.graph.worker_node import StepExecutionResult, WorkerNode

__all__ = [
# Goal
Expand Down
8 changes: 4 additions & 4 deletions core/framework/graph/code_sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
"""

import ast
import sys
import signal
from typing import Any
from dataclasses import dataclass, field
import sys
from contextlib import contextmanager
from dataclasses import dataclass, field
from typing import Any

# Safe builtins whitelist
SAFE_BUILTINS = {
Expand Down Expand Up @@ -216,7 +216,7 @@ def handler(signum, frame):
raise TimeoutError(f"Code execution timed out after {seconds} seconds")

# Only works on Unix-like systems
if hasattr(signal, 'SIGALRM'):
if hasattr(signal, "SIGALRM"):
old_handler = signal.signal(signal.SIGALRM, handler)
signal.alarm(seconds)
try:
Expand Down
4 changes: 2 additions & 2 deletions core/framework/graph/edge.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
given the current goal, context, and execution state.
"""

from typing import Any
from enum import Enum
from typing import Any

from pydantic import BaseModel, Field

Expand Down Expand Up @@ -238,7 +238,7 @@ def _llm_decide(

# Parse response
import re
json_match = re.search(r'\{[^{}]*\}', response.content, re.DOTALL)
json_match = re.search(r"\{[^{}]*\}", response.content, re.DOTALL)
if json_match:
data = json.loads(json_match.group())
proceed = data.get("proceed", False)
Expand Down
19 changes: 10 additions & 9 deletions core/framework/graph/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,26 @@
"""

import logging
from typing import Any, Callable
from collections.abc import Callable
from dataclasses import dataclass, field
from typing import Any

from framework.runtime.core import Runtime
from framework.graph.edge import GraphSpec
from framework.graph.goal import Goal
from framework.graph.node import (
NodeSpec,
FunctionNode,
LLMNode,
NodeContext,
NodeResult,
NodeProtocol,
SharedMemory,
LLMNode,
NodeResult,
NodeSpec,
RouterNode,
FunctionNode,
SharedMemory,
)
from framework.graph.edge import GraphSpec
from framework.graph.output_cleaner import CleansingConfig, OutputCleaner
from framework.graph.validator import OutputValidator
from framework.graph.output_cleaner import OutputCleaner, CleansingConfig
from framework.llm.provider import LLMProvider, Tool
from framework.runtime.core import Runtime


@dataclass
Expand Down
Loading
Loading