Skip to content

Commit ed14dd9

Browse files
Use pydantic ai
1 parent f4d0a0a commit ed14dd9

File tree

7 files changed

+1377
-70
lines changed

7 files changed

+1377
-70
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
# TODO
2+
returns library
3+
api error from backend common
4+
5+
16
# Transcribo Backend
27

38
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ dependencies = [
2424
"dcc-backend-common[fastapi]>=0.0.2",
2525
"dependency-injector>=4.48.3",
2626
"fastapi[standard]>=0.128.0",
27+
"pydantic-ai>=1.43.0",
2728
"returns>=0.26.0",
2829
]
2930

src/transcribo_backend/agents/__init__.py

Whitespace-only changes.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from openai import AsyncOpenAI
2+
from pydantic_ai import Agent, TextOutput
3+
from pydantic_ai.models.openai import OpenAIChatModel
4+
from pydantic_ai.providers.openai import OpenAIProvider
5+
6+
from transcribo_backend.utils.app_config import AppConfig
7+
8+
9+
def transform_to_swissgerman_style(text: str) -> str:
10+
return text.replace("ß", "ss")
11+
12+
13+
def create_summarize_agent(app_config: AppConfig) -> Agent:
14+
client = AsyncOpenAI(max_retries=3, base_url=app_config.llm_base_url, api_key=app_config.api_key)
15+
model = OpenAIChatModel(model_name=app_config.llm_model, provider=OpenAIProvider(openai_client=client))
16+
summarize_agent: Agent = Agent(
17+
model=model,
18+
output_type=TextOutput(transform_to_swissgerman_style),
19+
)
20+
21+
@summarize_agent.instructions
22+
def get_instructions() -> str:
23+
return """
24+
You are a meeting summary expert.
25+
You are given a transcript of a meeting and you need to summarize it.
26+
You need to summarize the meeting in a way that is easy to understand and use.
27+
You need to include the main points of the meeting, the decisions made, and the action items.
28+
You need to include the names of the participants.
29+
You use markdown to format the summary.
30+
Use the same language as used in the transcript to summarize the meeting.
31+
If you are not sure about the language, use German.
32+
"""
33+
34+
return summarize_agent
Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,19 @@
1-
from openai import AsyncOpenAI
2-
1+
from transcribo_backend.agents.summarize_agent import create_summarize_agent
32
from transcribo_backend.models.summary import Summary
43
from transcribo_backend.utils.app_config import AppConfig
54

6-
system_prompt = """
7-
You are a meeting summary expert.
8-
You are given a transcript of a meeting and you need to summarize it.
9-
You need to summarize the meeting in a way that is easy to understand and use.
10-
You need to include the main points of the meeting, the decisions made, and the action items.
11-
You need to include the names of the participants.
12-
You use markdown to format the summary.
13-
Use the same language as used in the transcript to summarize the meeting.
14-
If you are not sure about the language, use German.
15-
"""
16-
175

186
class SummarizationService:
197
def __init__(self, app_config: AppConfig):
208
self.app_config = app_config
9+
self.agent = create_summarize_agent(app_config)
2110

2211
async def summarize(self, transcript: str) -> Summary:
2312
"""
2413
Summarize a transcript of a meeting.
2514
"""
2615
try:
27-
client = AsyncOpenAI(api_key=self.app_config.api_key, base_url=self.app_config.api_key)
28-
models = await client.models.list()
29-
30-
if not models.data:
31-
raise ValueError("Could not find any models available from the API")
32-
33-
model = models.data[0].id
34-
except Exception as e:
35-
raise RuntimeError("Failed to initialize OpenAI client or fetch models.") from e
36-
37-
try:
38-
response = await client.chat.completions.create(
39-
model=model,
40-
messages=[{"role": "system", "content": system_prompt}, {"role": "user", "content": transcript}],
41-
)
42-
return Summary(summary=response.choices[0].message.content)
16+
result = await self.agent.run(transcript)
17+
return Summary(summary=result.output)
4318
except Exception as e:
4419
raise RuntimeError("Failed to generate summary.") from e
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from unittest.mock import AsyncMock, MagicMock, patch
2+
3+
import pytest
4+
5+
from transcribo_backend.models.summary import Summary
6+
from transcribo_backend.services.summarization_service import SummarizationService
7+
from transcribo_backend.utils.app_config import AppConfig
8+
9+
10+
@pytest.mark.asyncio
11+
async def test_summarize_calls_agent():
12+
# Mock AppConfig
13+
app_config = MagicMock(spec=AppConfig)
14+
15+
# Mock the Agent and its run method
16+
mock_agent = MagicMock()
17+
mock_run_result = MagicMock()
18+
mock_run_result.data = "This is a summary."
19+
mock_agent.run = AsyncMock(return_value=mock_run_result)
20+
21+
# Patch create_summarize_agent to return our mock agent
22+
with patch(
23+
"transcribo_backend.services.summarization_service.create_summarize_agent", return_value=mock_agent
24+
) as mock_create:
25+
service = SummarizationService(app_config)
26+
27+
# Verify agent creation was called
28+
mock_create.assert_called_once_with(app_config)
29+
30+
transcript = "Some meeting transcript."
31+
result = await service.summarize(transcript)
32+
33+
# Verify run was called
34+
mock_agent.run.assert_called_once_with(transcript)
35+
36+
# Verify result
37+
assert isinstance(result, Summary)
38+
assert result.summary == "This is a summary."

0 commit comments

Comments
 (0)