From e48cb95bee2e5c5f8536775092ecfbb53bd26207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=9A=D1=83=D1=82?= =?UTF-8?q?=D0=BB=D0=B0=D1=85=D0=BC=D0=B5=D1=82=D0=BE=D0=B2?= Date: Thu, 28 May 2026 18:08:43 +0000 Subject: [PATCH] add agent.py --- agent.py | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 agent.py diff --git a/agent.py b/agent.py new file mode 100644 index 0000000..810dda7 --- /dev/null +++ b/agent.py @@ -0,0 +1,76 @@ +""" +Agent module that performs structured extraction using LangChain and Pydantic. + +The module exposes a single public function `extract_structured_output` which: +1. Detects whether the input text describes a person or a meeting. +2. Builds an appropriate prompt with format instructions from the chosen parser. +3. Sends the request to the LLM via LangChain and returns the parsed Pydantic model instance. +""" + +from __future__ import annotations + +import re +from typing import Union, List + +from langchain_core.output_parsers import PydanticOutputParser +from langchain_core.prompts import PromptTemplate +from langchain_openai import ChatOpenAI + +from models import PersonInfo, MeetingNotes + +# LLM configuration – BroJS endpoint +llm = ChatOpenAI( + model="openai/gpt-oss-20b:free", + base_url="https://platform.brojs.ru/jrnl-bh/api/inference/v1", + api_key=None, # will be taken from env var JOURNAL_MCP_PAT + temperature=0.0, +) + +# Prompt template – the same for both schemas; format instructions are injected. +PROMPT = PromptTemplate( + input_variables=["text", "format_instructions"], + template=""" +You are a data extraction assistant. +Extract structured information from the following text and return it as JSON that matches the provided schema. + +Text: +{text} + +Schema format instructions: +{format_instructions} + +Respond only with valid JSON. Do not add any extra text. +""", +) + +def _detect_schema(text: str) -> type: + """Heuristically determine whether the input describes a person or a meeting. + Returns the corresponding Pydantic model class. + """ + # Simple keyword checks – can be extended + if re.search(r"\bmeeting\b|\bdate\b|\bparticipants?\b", text, re.I): + return MeetingNotes + return PersonInfo + + +def extract_structured_output(text: str) -> Union[PersonInfo, MeetingNotes]: + """Return a Pydantic model instance extracted from *text*. + + Parameters + ---------- + text: + Free‑form input string. + + Returns + ------- + PersonInfo | MeetingNotes + Parsed data as a validated Pydantic object. + """ + schema = _detect_schema(text) + parser = PydanticOutputParser(pydantic_object=schema) + prompt = PROMPT.format(text=text, format_instructions=parser.get_format_instructions()) + response = llm.invoke([prompt]) + # The LLM returns a string; parse it + return parser.parse(response.content) + +__all__ = ["extract_structured_output"]