Python SDK
데코레이터와 워크플로우 빌더를 사용하여 Python으로 JamJet 워크플로우를 작성하세요.
Python SDK
JamJet Python SDK를 사용하면 YAML 대신 Python으로 워크플로우를 작성할 수 있습니다. 두 방식 모두 동일한 IR로 컴파일되며 동일한 Rust 런타임에서 실행됩니다.
설치
pip install jamjet데코레이터 API
데코레이터 API는 워크플로우를 작성하는 가장 간결한 방법입니다. 함수에 @node를 추가하면 JamJet이 함수 시그니처와 반환 타입에서 노드 타입을 자동으로 추론합니다.
from jamjet import workflow, node, State
@workflow(id="hello-agent", version="0.1.0")
class HelloWorkflow:
@node(start=True)
async def think(self, state: State) -> State:
response = await self.model(
model="claude-haiku-4-5-20251001",
prompt=f"Answer clearly: {state['query']}",
)
return {"answer": response.text}실행 방법:
import asyncio
from jamjet import JamJetClient
async def main():
client = JamJetClient() # connects to http://localhost:7700
result = await client.run(
HelloWorkflow,
input={"query": "What is JamJet?"}
)
print(result.state["answer"])
asyncio.run(main())워크플로우 빌더
더 세밀한 제어가 필요하다면 빌더 API를 사용하세요:
from jamjet.workflows import WorkflowBuilder, ModelNode, ToolNode, BranchNode
wf = (
WorkflowBuilder("research-agent", version="0.2.0")
.state_schema(query=str, results=list, answer=str)
.add_node(
ToolNode("search")
.server("brave-search")
.tool("web_search")
.arguments({"query": "{{ state.query }}", "count": 5})
.output_key("results")
.next("draft")
)
.add_node(
ModelNode("draft")
.model("claude-sonnet-4-6")
.prompt("""
Search results: {{ state.results | join('\\n') }}
Answer: {{ state.query }}
""")
.output_key("answer")
.next("end")
)
.start("search")
.build()
)State 접근
State는 타입이 지정된 딕셔너리 유사 객체입니다. state["key"] 또는 state.key로 키에 접근할 수 있습니다:
@node
async def process(self, state: State) -> State:
query = state["query"] # raises KeyError if missing
context = state.get("context") # returns None if missing
# Return a partial state patch — only keys you include are updated
return {"answer": "...", "confidence": 0.95}tip: 노드는 전체 state가 아닌 state 패치를 반환합니다. 업데이트할 키만 반환하면 됩니다. 기존 키는 그대로 유지됩니다.
모델 호출
노드 내부에서 self.model()을 사용하여 LLM을 호출하세요:
@node
async def think(self, state: State) -> State:
response = await self.model(
model="claude-sonnet-4-6",
prompt=f"Answer: {state['query']}",
system="You are concise and accurate.",
temperature=0.3,
max_tokens=512,
)
# response.text — 전체 텍스트
# response.usage.input_tokens
# response.usage.output_tokens
# response.model
return {"answer": response.text}툴 호출 (MCP)
연결된 MCP 서버의 툴을 호출하려면 self.tool()을 사용하세요:
@node
async def search(self, state: State) -> State:
result = await self.tool(
server="brave-search",
tool="web_search",
arguments={"query": state["query"], "count": 5},
)
return {"results": result.content}HTTP 호출
@node
async def fetch(self, state: State) -> State:
result = await self.http(
method="GET",
url=f"https://api.example.com/items/{state['item_id']}",
headers={"Authorization": f"Bearer {self.env('API_KEY')}"},
)
return {"raw": result.json()}분기
from jamjet import node, branch
@node
@branch(
conditions=[
("state['confidence'] >= 0.9", "done"),
("state['confidence'] >= 0.5", "refine"),
],
default="escalate",
)
async def route(self, state: State) -> State:
return {} # 분기 노드는 기존 상태를 읽습니다 — 출력이 필요하지 않습니다병렬 실행
from jamjet.workflows import ParallelNode
.add_node(
ParallelNode("gather")
.branches(["search", "fetch-docs", "check-cache"])
.join("synthesize")
)재시도 정책
from jamjet.workflows import RetryPolicy
ModelNode("think")
.model("claude-haiku-4-5-20251001")
.prompt("...")
.retry(RetryPolicy(
max_attempts=3,
backoff="exponential",
delay_ms=500,
))워크플로우 실행
from jamjet import JamJetClient
client = JamJetClient(base_url="http://localhost:7700")
# 실행하고 완료될 때까지 대기
result = await client.run(wf, input={"query": "..."})
print(result.state)
print(result.execution_id)
print(result.duration_ms)
# 실행 후 대기 없이 진행 — 즉시 실행 ID 반환
exec_id = await client.submit(wf, input={"query": "..."})
# 상태 폴링
status = await client.get_execution(exec_id)
print(status.status) # running | completed | failed
# 이벤트를 실시간으로 스트리밍
async for event in client.stream(wf, input={"query": "..."}):
print(event.type, event.node_id)타입 어노테이션
SDK는 완전한 타입 스텁을 제공합니다. strict 모드에서:
from jamjet import State, NodeResult
from typing import TypedDict
class MyState(TypedDict):
query: str
answer: str
confidence: float
@node(start=True)
async def think(self, state: MyState) -> NodeResult[MyState]:
...
return NodeResult(answer="...", confidence=0.9)설정
from jamjet import JamJetClient, JamJetConfig
client = JamJetClient(config=JamJetConfig(
base_url="http://localhost:7700",
api_key="YOUR_API_KEY", # for hosted/production
timeout_ms=30_000,
default_model="claude-haiku-4-5-20251001",
))또는 환경 변수를 통해:
export JAMJET_URL=http://localhost:7700
export JAMJET_API_KEY=...