add main.py
This commit is contained in:
@@ -0,0 +1,131 @@
|
|||||||
|
"""
|
||||||
|
Main entry point for the Stream‑mode LangChain agent.
|
||||||
|
|
||||||
|
The script demonstrates how to replace a single ``invoke`` call with a streaming
|
||||||
|
``stream`` interface. The output is printed token by token so that the user can
|
||||||
|
see the agent “think” in real time.
|
||||||
|
|
||||||
|
Three example invocations are provided:
|
||||||
|
1. A simple question that requires no tool calls.
|
||||||
|
2. A request that triggers the ``get_price`` tool.
|
||||||
|
3. A multi‑step conversation that uses the same tool twice.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
from typing import Any, Dict, Tuple
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# LLM configuration – BroJS provider
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
from langchain_openai import ChatOpenAI
|
||||||
|
|
||||||
|
llm = ChatOpenAI(
|
||||||
|
model="openai/gpt-oss-20b:free",
|
||||||
|
base_url="https://platform.brojs.ru/jrnl-bh/api/inference/v1",
|
||||||
|
api_key=os.getenv("JOURNAL_MCP_PAT"),
|
||||||
|
temperature=0.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Tool definition – a tiny mock price lookup
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
from langchain.tools import tool
|
||||||
|
|
||||||
|
@tool
|
||||||
|
def get_price(product: str, city: str) -> str:
|
||||||
|
"""Return a fake price for *product* in *city*.
|
||||||
|
|
||||||
|
The function is intentionally simple; it only demonstrates how the agent
|
||||||
|
can call tools while streaming. In a real project this would query an API
|
||||||
|
or database.
|
||||||
|
"""
|
||||||
|
prices = {
|
||||||
|
("milk", "kazan"): "89",
|
||||||
|
("bread", "kazan"): "45",
|
||||||
|
("coffee", "moscow"): "120",
|
||||||
|
}
|
||||||
|
key = (product.lower(), city.lower())
|
||||||
|
price = prices.get(key, "unknown")
|
||||||
|
return f"{price} rubles"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Agent construction – zero‑shot react description
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
from langchain.agents import create_agent
|
||||||
|
from langchain_core.messages import HumanMessage
|
||||||
|
|
||||||
|
agent = create_agent(
|
||||||
|
llm=llm,
|
||||||
|
tools=[get_price],
|
||||||
|
system_prompt="You are a helpful assistant that can call the get_price tool.",
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Streaming helper functions
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
def format_message(message: Any) -> str:
|
||||||
|
"""Return human‑readable representation of a message.
|
||||||
|
|
||||||
|
If the message contains content we return it directly. Otherwise we
|
||||||
|
construct a string that shows the tool call.
|
||||||
|
"""
|
||||||
|
if getattr(message, "content", None):
|
||||||
|
return message.content
|
||||||
|
# Tool call – ``message.tool_calls`` is a list of dicts
|
||||||
|
calls = [f"{c['name']}({c['args']})" for c in message.tool_calls]
|
||||||
|
return ", ".join(calls)
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Core streaming logic
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
def stream_and_print(prompt: str) -> None:
|
||||||
|
"""Invoke the agent with ``stream`` and print tokens as they arrive.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
prompt: str
|
||||||
|
The user message to send to the agent.
|
||||||
|
"""
|
||||||
|
# Start streaming – we want both token‑level messages and state updates
|
||||||
|
stream = agent.stream(
|
||||||
|
{"messages": [HumanMessage(content=prompt)]},
|
||||||
|
stream_mode=["messages", "updates"],
|
||||||
|
)
|
||||||
|
|
||||||
|
current_step: int | None = None
|
||||||
|
|
||||||
|
for chunk_type, chunk_data in stream:
|
||||||
|
if chunk_type == "messages":
|
||||||
|
# ``chunk_data`` is a tuple (message, meta)
|
||||||
|
message, meta = chunk_data # type: ignore[assignment]
|
||||||
|
step = meta.get("langgraph_step")
|
||||||
|
if current_step != step:
|
||||||
|
current_step = step
|
||||||
|
print("\n--- Step {} ---\n".format(step), end="", flush=True)
|
||||||
|
if message.content:
|
||||||
|
print(message.content, end="", flush=True)
|
||||||
|
elif chunk_type == "updates":
|
||||||
|
# ``chunk_data`` contains the finished state of a step
|
||||||
|
model = chunk_data.get("model")
|
||||||
|
if model and model["messages"]:
|
||||||
|
last_msg = model["messages"][-1]
|
||||||
|
print(format_message(last_msg), end="", flush=True)
|
||||||
|
print("\n--- End ---\n")
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Example usage – three distinct scenarios
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
if __name__ == "__main__":
|
||||||
|
examples = [
|
||||||
|
"What is the capital of France?",
|
||||||
|
"How much does milk cost in Kazan?",
|
||||||
|
"I need prices for bread and coffee in Kazan and Moscow.",
|
||||||
|
]
|
||||||
|
|
||||||
|
for i, ex in enumerate(examples, 1):
|
||||||
|
print(f"\n=== Example {i} ===")
|
||||||
|
stream_and_print(ex)
|
||||||
|
|
||||||
|
# End of file
|
||||||
Reference in New Issue
Block a user