From b5ad3c10281b804c936f903d516d2e8beab9982a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D1=8C=D1=8F=205f1b81b8-4f5d-11e8-9c2d-fa7ae01?= =?UTF-8?q?bbebc?= Date: Sat, 27 Jun 2026 13:52:15 +0000 Subject: [PATCH] add: main.py --- main.py | 146 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 main.py diff --git a/main.py b/main.py new file mode 100644 index 0000000..ca565b8 --- /dev/null +++ b/main.py @@ -0,0 +1,146 @@ +import os +import asyncio +import argparse +from dotenv import load_dotenv + +from pydantic import BaseModel, Field +from langchain_openai import ChatOpenAI +from langchain_core.messages import HumanMessage +from langchain.tools import tool +from langchain_core.output_parsers import PydanticOutputParser +from langchain_core.prompts import PromptTemplate + +from deepagents import create_deep_agent +from deepagents.backends import FilesystemBackend, LocalShellBackend, CompositeBackend + +# Load API key from .env +load_dotenv() + +# LLM configuration – OpenRouter +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, +) + +# Pydantic models +class PersonInfo(BaseModel): + name: str = Field(description="Full name of the person") + age: int | None = Field(description="Age of the person, optional", default=None) + profession: str = Field(description="Current profession or role") + skills: list[str] = Field(description="List of skills or technologies") + +class MeetingNotes(BaseModel): + date: str = Field(description="Date of the meeting (YYYY-MM-DD)") + participants: list[str] = Field(description="Names of participants") + topics: list[str] = Field(description="Main discussion topics") + decisions: list[str] = Field(description="Decisions made during the meeting") + next_steps: list[str] = Field(description="Action items for next steps") + +# Prompt templates +person_prompt = PromptTemplate.from_template( + """ +Extract the following information from the text: name, age (optional), profession, skills. +The text: {input_text} +Output must be in JSON format according to the following schema: +{format_instructions} +""" +) + +meeting_prompt = PromptTemplate.from_template( + """ +Extract the following information from the text: date, participants, topics, decisions, next_steps. +The text: {input_text} +Output must be in JSON format according to the following schema: +{format_instructions} +""" +) + +# Output parsers +person_parser = PydanticOutputParser(pydantic_object=PersonInfo) +meeting_parser = PydanticOutputParser(pydantic_object=MeetingNotes) + +# Simple heuristic to decide which schema to use + +def determine_schema(text: str) -> str: + lower = text.lower() + meeting_keywords = ["meeting", "participants", "date", "topics", "decisions", "next steps", "next_step", "next_step"] + if any(k in lower for k in meeting_keywords): + return "meeting" + return "person" + +# Summary generation + +def create_summary(parsed: BaseModel, schema: str) -> str: + if schema == "meeting": + return ( + f"Meeting on {parsed.date} with participants {', '.join(parsed.participants)}. " + f"Topics: {', '.join(parsed.topics)}. Decisions: {', '.join(parsed.decisions)}. " + f"Next steps: {', '.join(parsed.next_steps)}." + ) + else: + age = parsed.age if parsed.age is not None else "N/A" + return ( + f"{parsed.name}, age {age}, profession {parsed.profession}. " + f"Skills: {', '.join(parsed.skills)}." + ) + +# Tool that performs extraction +@tool +def process_text(input_text: str) -> str: + schema = determine_schema(input_text) + if schema == "meeting": + prompt = meeting_prompt + parser = meeting_parser + else: + prompt = person_prompt + parser = person_parser + chain = prompt | llm | parser + parsed = chain.invoke({"input_text": input_text}) + summary = create_summary(parsed, schema) + return parsed.model_dump_json(indent=2) + "\n\nSummary: " + summary + +# Backend for the agent +backend = CompositeBackend([ + LocalShellBackend(workspace_dir="./workspace"), + FilesystemBackend(), +]) + +# Create the deep agent +agent = create_deep_agent( + model=llm, + tools=[process_text], + backend=backend, + system_prompt="You are a helpful agent that extracts structured data from text.", +) + +# CLI entry point +async def main(): + parser = argparse.ArgumentParser(description="Extract structured data from text.") + parser.add_argument("--example", choices=["person", "meeting"], help="Run example") + parser.add_argument("--text", type=str, help="Input text") + args = parser.parse_args() + + if args.example: + if args.example == "person": + input_text = "Анна, 28 лет, Python-разработчик. Навыки: FastAPI, Docker." + else: + input_text = ( + "Дата: 2023-05-01. Участники: Иван, Мария. Темы: проект X, бюджет. " + "Решения: утвердить бюджет. Next steps: подготовить план." + ) + elif args.text: + input_text = args.text + else: + input_text = input("Enter text: ") + + result = await agent.ainvoke( + {"messages": [HumanMessage(content=input_text)]}, + {"configurable": {"thread_id": "session-1"}}, + ) + output = result["messages"][-1].content + print(output) + +if __name__ == "__main__": + asyncio.run(main())