Branch¶
class Branch(Element, Relational)
Single conversation thread: manages message history, tools, model access, and LLM operations. Most SDK usage starts here.
Constructor¶
branch = li.Branch(
chat_model=li.iModel(model="gpt-4o"),
system="You are a research assistant.",
name="researcher",
)
| Param | Type | Default | Notes |
|---|---|---|---|
user | SenderRecipient \| None | None | Branch owner/sender identity |
name | str \| None | None | Human label |
system | System \| JsonValue | None | System prompt (str or dict) |
chat_model | iModel \| dict \| str | None | Primary model for chat / communicate / operate |
parse_model | iModel \| dict \| str | None | Model used for parse() retries |
imodel | iModel | None | Deprecated alias for chat_model |
tools | FuncTool \| list[FuncTool] | None | Pre-register tools on construction |
messages | Pile[RoledMessage] | None | Restore prior conversation history |
logs | Pile[Log] | None | Restore prior activity logs |
log_config | DataLoggerConfig \| dict | None | Log output configuration |
system_datetime | bool \| str | None | Inject current timestamp into system prompt |
system_template | str \| None | None | Jinja2-style system template string |
system_template_context | dict | None | Variables for system_template |
use_lion_system_message | bool | False | Prepend LIONAGI system preamble |
Properties¶
| Property | Type | Writable | Notes |
|---|---|---|---|
system | System \| None | No | Active system message |
messages | Pile[RoledMessage] | No | Full conversation history |
logs | Pile[Log] | No | Activity log pile |
chat_model | iModel | Yes | Swap chat provider at runtime |
parse_model | iModel | Yes | Swap parse provider at runtime |
tools | dict[str, Tool] | No | Registered tool registry |
msgs | MessageManager | No | Internal message manager |
acts | ActionManager | No | Internal action manager |
mdls | iModelManager | No | Internal model manager |
Operations¶
operate() — universal structured operation¶
from pydantic import BaseModel
class Summary(BaseModel):
title: str
key_points: list[str]
result = await branch.operate(
instruction="Summarize this paper: ...",
response_format=Summary,
actions=True,
tools=["search"],
action_strategy="concurrent",
)
# result: Summary instance
Routes through the Middle protocol: communicate for API endpoints, run_and_collect for CLI endpoints. Supports tool calling, structured output, and streaming persistence.
| Param | Type | Default | Notes |
|---|---|---|---|
instruction | Instruction \| JsonValue | None | User message |
instruct | Instruct | None | Alternative to instruction — structured instruct object |
guidance | JsonValue | None | Additional guidance injected into instruction |
context | JsonValue | None | Prompt context visible to the model |
response_format | type[BaseModel] | None | Parse output into this Pydantic model |
actions | bool | False | Enable tool calling |
tools | ToolRef | None | Subset of registered tools to expose |
invoke_actions | bool | True | Auto-invoke tool calls returned by model |
action_strategy | "sequential" \| "concurrent" | "concurrent" | Tool execution order |
field_models | list[FieldModel] | None | Dynamic field extensions |
stream_persist | bool | False | Write JSONL chunks live (CLI endpoints) |
persist_dir | str \| None | None | Directory for JSONL chunks |
middle | Middle \| None | None | Override default routing |
handle_validation | "raise" \| "return_value" \| "return_none" | "return_value" | Parse failure behavior |
chat_model | iModel | None | Override branch's chat model for this call |
parse_model | iModel | None | Override branch's parse model |
skip_validation | bool | False | Skip response parsing entirely |
reason | bool | False | Enable chain-of-thought reasoning field |
sender / recipient | SenderRecipient | None | Override message identity |
Returns: list | BaseModel | None | dict | str
For cookbook usage, see Research synthesis.
communicate() — single-turn with history accumulation¶
result = await branch.communicate(
"What are the main causes of climate change?",
response_format=None, # returns str
)
# adds both user message and assistant response to branch.messages
Simpler than operate() — no tool calling. Accumulates messages in history automatically. Use when you need history building without tool invocation.
| Param | Type | Default | Notes |
|---|---|---|---|
instruction | Instruction \| JsonValue | None | User message |
guidance | JsonValue | None | Additional guidance |
context | JsonValue | None | Prompt context |
plain_content | str | None | Bypass instruction formatting |
response_format | type[BaseModel] | None | Parse output into Pydantic model |
request_fields | dict \| list[str] | None | Request specific fields from model |
num_parse_retries | int | 3 | Parse retry attempts |
clear_messages | bool | False | Clear history before this turn |
Returns: BaseModel | dict | str | None
chat() — raw model invocation¶
instruction_msg, response_msg = await branch.chat(
"Draft an outline for a research paper on RAG.",
response_format=None,
)
# does NOT add to branch.messages — caller manages history
Low-level building block. Does not add messages to history. Returns (Instruction, AssistantResponse) tuple — use when you need explicit history control.
run() — streaming CLI endpoint¶
async for msg in branch.run("Write a detailed analysis of..."):
if hasattr(msg, "content"):
print(msg.content, end="", flush=True)
Async generator — yields RoledMessage objects as chunks arrive. Requires a CLI endpoint model (e.g., iModel(provider="claude_code", model="sonnet")).
| Param | Type | Default | Notes |
|---|---|---|---|
instruction | str | "" | User message |
chat_model | iModel \| None | None | Override to CLI endpoint model |
stream_persist | bool | False | Write JSONL chunks to disk |
persist_dir | str \| None | None | JSONL output directory |
response_format | type | None | Parse final accumulated text |
parse() — structured extraction from text¶
class Verdict(BaseModel):
score: int
reasoning: str
verdict = await branch.parse(
text='{"score": 8, "reasoning": "Strong methodology"}',
response_format=Verdict,
handle_validation="raise",
)
Extracts structured data from raw text without a new LLM call (unless retries are needed). Fuzzy key matching is enabled by default — handles minor key name variations from the model.
| Param | Type | Default | Notes |
|---|---|---|---|
text | str | required | Raw text to parse |
response_format | type[BaseModel] | None | Target Pydantic model |
handle_validation | "raise" \| "return_value" \| "return_none" | "return_value" | Failure behavior |
max_retries | int | 3 | LLM retry attempts on parse failure |
fuzzy_match | bool | True | Enable fuzzy key matching |
similarity_threshold | float | 0.85 | Minimum similarity for key matching |
Returns: BaseModel | dict | str | None
act() — tool execution¶
responses = await branch.act(
action_request=[{"tool": "search", "arguments": {"query": "LLM benchmarks 2025"}}],
strategy="concurrent",
)
Directly invoke tool calls. Takes ActionRequest, dict, or list.
| Param | Type | Default | Notes |
|---|---|---|---|
action_request | list \| ActionRequest \| BaseModel \| dict | required | Tool call(s) to execute |
strategy | "concurrent" \| "sequential" | "concurrent" | Execution order |
suppress_errors | bool | True | Catch tool errors instead of raising |
verbose_action | bool | False | Log each invocation |
Returns: list[ActionResponse]
ReAct() — think-act-observe reasoning loops¶
result = await branch.ReAct(
instruct={"instruction": "Find the latest papers on diffusion models and summarize."},
tools=["search", "read_url"],
max_extensions=5,
response_format=ResearchReport,
)
Multi-round reasoning with tool use. Iterates until max_extensions or a terminal response.
| Param | Type | Default | Notes |
|---|---|---|---|
instruct | Instruct \| dict | required | Initial instruction |
tools | Any | None | Tools to expose (defaults to all registered) |
response_format | type[BaseModel] | None | Final output schema |
max_extensions | int \| None | 3 | Max reasoning iterations |
reasoning_effort | "low" \| "medium" \| "high" | None | Reasoning depth hint |
return_analysis | bool | False | Also return list of intermediate steps |
verbose | bool | False | Print each iteration |
Returns: Any or (Any, list) if return_analysis=True.
For a full working example, see Research synthesis.
interpret() — prompt rewriting¶
refined = await branch.interpret(
"llm stuff for code gen",
domain="software engineering",
style="precise and technical",
)
# refined: "Explain LLM-based code generation techniques for production use."
Rewrites raw user input into a refined prompt. Does not add to history.
Tool management¶
# register functions
branch.register_tools([search_fn, read_file_fn])
# register with update=True to overwrite existing
branch.register_tools(new_search_fn, update=True)
# register an API endpoint as a callable tool
branch.connect(
provider="openai",
endpoint="chat",
name="gpt_tool",
description="GPT-4o as a callable tool",
)
Context manager¶
async with li.Branch(chat_model=li.iModel(model="gpt-4o")) as branch:
result = await branch.operate(instruction="Analyze this dataset: ...")
# logs auto-dumped on exit
Serialization¶
# round-trip
data = branch.to_dict()
restored = li.Branch.from_dict(data)
# messages as DataFrame
df = branch.to_df()
# clone (sync or async)
b2 = branch.clone()
b2 = await branch.aclone(sender=new_sender_id)
# dump logs
branch.dump_logs(clear=True, persist_path="./logs/session.json")
await branch.adump_logs(clear=False)
Next: Session — manage multiple branches