From 5a92b34608a915495e7c516a71a8b61a2b5ea7ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=94=D0=B0=D0=BD=D0=B8=D0=B8=D0=BB=20=D0=92=D0=B8=D0=BA?= =?UTF-8?q?=D1=82=D0=BE=D1=80=D0=BE=D0=B2?= Date: Tue, 30 Jun 2026 16:11:21 +0000 Subject: [PATCH] =?UTF-8?q?add:=20main.py=20=E2=80=94=20=D0=AD=D0=BA=D0=B7?= =?UTF-8?q?=D0=B0=D0=BC=D0=B5=D0=BD:=20=D0=A1=D1=82=D1=80=D1=83=D0=BA?= =?UTF-8?q?=D1=82=D1=83=D1=80=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=BD?= =?UTF-8?q?=D1=8B=D0=B9=20=D0=B2=D1=8B=D0=B2=D0=BE=D0=B4=20(Pydantic)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.py | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 main.py diff --git a/main.py b/main.py new file mode 100644 index 0000000..de2c3df --- /dev/null +++ b/main.py @@ -0,0 +1,136 @@ +import os +import sys +import asyncio +from dotenv import load_dotenv +from langchain_openai import ChatOpenAI +from langchain_core.messages import HumanMessage +from langchain.tools import tool +from deepagents import create_deep_agent +from deepagents.backends import FilesystemBackend, LocalShellBackend, CompositeBackend +from pydantic import BaseModel, Field +from langchain_core.output_parsers import PydanticOutputParser +from langchain_core.prompts import PromptTemplate + +load_dotenv() + +# Pydantic models +class PersonInfo(BaseModel): + name: str = Field(description="Full name of the person") + age: int | None = Field(description="Age in years, optional", default=None) + profession: str = Field(description="Current profession") + skills: list[str] = Field(description="List of skills") + +class MeetingNotes(BaseModel): + date: str = Field(description="Date of the meeting") + participants: list[str] = Field(description="List of participants") + topics: list[str] = Field(description="Discussion topics") + decisions: list[str] = Field(description="Decisions made") + next_steps: list[str] = Field(description="Next steps to be taken") + +# LLM configuration +llm = ChatOpenAI( + model="openai/gpt-oss-20b:free", + base_url="https://openrouter.ai/api/v1", + api_key=os.getenv("OPENAI_API_KEY"), + temperature=0.0, +) + +# Backend for deepagents +backend = CompositeBackend([ + LocalShellBackend(workspace_dir="./workspace"), + FilesystemBackend(), +]) + +# Parsers +person_parser = PydanticOutputParser(pydantic_object=PersonInfo) +meeting_parser = PydanticOutputParser(pydantic_object=MeetingNotes) + +# Prompt template +prompt_template = PromptTemplate( + input_variables=["format_instructions", "text"], + template=( + "Extract structured data from the following text. " + "Follow the format:\n{format_instructions}\n\nText:\n{text}\n\nOutput:" + ), +) + +# Helper to decide which parser to use +def choose_parser(text: str): + lower = text.lower() + if any(word in lower for word in ["age", "years", "profession", "skills", "skill"]): + return person_parser + if any(word in lower for word in ["meeting", "participants", "topics", "decisions", "next steps", "next_step"]): + return meeting_parser + # Default to person_parser + return person_parser + +# Tool for extraction +@tool +def extract_structured(text: str) -> str: + """ + Extract structured data from the given text and return a JSON string. + """ + parser = choose_parser(text) + format_instructions = parser.get_format_instructions() + prompt = prompt_template.format(format_instructions=format_instructions, text=text) + raw_output = llm.invoke(prompt).content + try: + parsed_obj = parser.parse(raw_output) + return parsed_obj.model_dump_json() + except Exception as e: + return f"Error parsing output: {e}" + +# Create the agent +agent = create_deep_agent( + model=llm, + tools=[extract_structured], + backend=backend, + system_prompt="You are a helpful agent that extracts structured data from text.", +) + +# CLI logic +async def run_agent(text: str): + result = await agent.ainvoke( + {"messages": [HumanMessage(content=text)]}, + {"configurable": {"thread_id": "session-1"}}, + ) + output = result["messages"][-1].content + try: + data = PersonInfo.model_validate_json(output) + obj_type = "PersonInfo" + except Exception: + try: + data = MeetingNotes.model_validate_json(output) + obj_type = "MeetingNotes" + except Exception: + print("Failed to parse JSON output.") + return + print("\nParsed object:") + print(data.model_dump()) + print("\nSummary:") + if obj_type == "PersonInfo": + print(f"{obj_type}: {data.name}, age={data.age}, profession={data.profession}, skills={data.skills}") + else: + print(f"{obj_type}: date={data.date}, participants={data.participants}, topics={data.topics}, decisions={data.decisions}, next_steps={data.next_steps}") + +def main(): + if len(sys.argv) > 1: + input_text = " ".join(sys.argv[1:]) + asyncio.run(run_agent(input_text)) + else: + examples = [ + ( + "Анна, 28 лет, Python-разработчик. Навыки: FastAPI, Docker.", + "PersonInfo example", + ), + ( + "Встреча 12.09.2026. Участники: Иван, Мария. Темы: проект X, бюджет. Решения: увеличить бюджет. Следующие шаги: подготовить отчёт.", + "MeetingNotes example", + ), + ] + for text, title in examples: + print(f"\n=== {title} ===") + asyncio.run(run_agent(text)) + +if __name__ == "__main__": + main() \ No newline at end of file