Files
task-6a1865008a94f887e50d471c/main.py
T
2026-05-28 17:35:44 +00:00

162 lines
5.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python
"""Structured output extraction with LangChain and Pydantic.
This script demonstrates how to parse a freetext description of either a person
or a meeting into a validated Pydantic model using LangChain's structured
output facilities. It can be run from the command line with a sample text or
with user input.
"""
import os
import sys
from typing import List, Union
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field
# ---------------------------------------------------------------------------
# 1. Pydantic models
# ---------------------------------------------------------------------------
class PersonInfo(BaseModel):
name: str = Field(..., description="Full name of the person")
age: int | None = Field(None, description="Age in years, optional")
profession: str = Field(..., description="Current profession or job title")
skills: List[str] = Field(..., description="List of professional skills")
class MeetingNotes(BaseModel):
date: str = Field(..., description="Date of the meeting in ISO format or natural language")
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 to be completed after the meeting")
# ---------------------------------------------------------------------------
# 2. LangChain LLM setup
# ---------------------------------------------------------------------------
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,
)
# ---------------------------------------------------------------------------
# 3. Prompt templates
# ---------------------------------------------------------------------------
person_prompt = PromptTemplate(
input_variables=["text"],
template="""
You are an assistant that extracts structured data about a person from the following text.
Text: {text}
Return the data as a JSON object that matches the PersonInfo schema.
{format_instructions}
""",
)
meeting_prompt = PromptTemplate(
input_variables=["text"],
template="""
You are an assistant that extracts structured data about a meeting from the following text.
Text: {text}
Return the data as a JSON object that matches the MeetingNotes schema.
{format_instructions}
""",
)
# ---------------------------------------------------------------------------
# 4. Output parsers
# ---------------------------------------------------------------------------
person_parser = PydanticOutputParser(pydantic_object=PersonInfo)
meeting_parser = PydanticOutputParser(pydantic_object=MeetingNotes)
# ---------------------------------------------------------------------------
# 5. Heuristic to decide which schema to use
# ---------------------------------------------------------------------------
def choose_schema(text: str) -> str:
"""Return 'person' or 'meeting' based on simple keyword heuristics.
The heuristic is intentionally simple: if the text contains words that
are more likely to appear in a meeting description (e.g. "meeting", "agenda",
"participants", "decisions", "action items") we choose the meeting schema.
Otherwise we default to the person schema.
"""
meeting_keywords = {
"meeting",
"agenda",
"participants",
"decisions",
"action items",
"next steps",
"topics",
"date",
}
text_lower = text.lower()
if any(word in text_lower for word in meeting_keywords):
return "meeting"
return "person"
# ---------------------------------------------------------------------------
# 6. Main extraction function
# ---------------------------------------------------------------------------
def extract(text: str) -> Union[PersonInfo, MeetingNotes]:
schema_type = choose_schema(text)
if schema_type == "person":
prompt = person_prompt
parser = person_parser
else:
prompt = meeting_prompt
parser = meeting_parser
chain = prompt | llm | parser
result = chain.invoke({"text": text})
return result
# ---------------------------------------------------------------------------
# 7. CLI
# ---------------------------------------------------------------------------
def main():
if len(sys.argv) > 1:
# First argument is the text to parse
input_text = " ".join(sys.argv[1:])
else:
# Provide two builtin examples
examples = {
"person": "Анна, 28 лет, Python-разработчик. Навыки: FastAPI, Docker.",
"meeting": "Встреча 12.09.2026. Участники: Иван, Мария. Темы: проект X, бюджет. Решения: утвердить бюджет. Next steps: подготовить презентацию.",
}
print("Choose example: 1 - person, 2 - meeting, or type your own text.")
choice = input("> ").strip()
if choice == "1":
input_text = examples["person"]
elif choice == "2":
input_text = examples["meeting"]
else:
input_text = choice
print("\nInput text:\n" + input_text + "\n")
try:
obj = extract(input_text)
print("\nParsed object (model_dump):")
print(obj.model_dump(indent=2))
print("\nSummary: ", obj)
except Exception as e:
print("Error during parsing:", e)
if __name__ == "__main__":
main()