2026-03-25 21:10:10 +00:00
|
|
|
import asyncio
|
|
|
|
|
import anthropic
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AnthropicClient():
|
|
|
|
|
def __init__(self, api_key: str):
|
|
|
|
|
self._client = anthropic.Anthropic(api_key=api_key)
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def new(cls, api_key: str) -> "AnthropicClient":
|
|
|
|
|
return cls(api_key)
|
|
|
|
|
|
|
|
|
|
def _create_summarise_text_system_prompt(
|
|
|
|
|
self,
|
|
|
|
|
complexity_level: str,
|
|
|
|
|
from_language: str,
|
|
|
|
|
to_language: str,
|
|
|
|
|
length_preference="200-400 words",
|
|
|
|
|
) -> str:
|
|
|
|
|
return (
|
|
|
|
|
f"You are a language learning content creator.\n"
|
2026-03-27 09:45:44 +00:00
|
|
|
f"You generate markdown summaries of user-provided content.\n"
|
|
|
|
|
f"You will provide content in {to_language} at {complexity_level} proficiency level on the CEFR scale.\n"
|
2026-03-25 21:10:10 +00:00
|
|
|
f"The text you generate will:\n"
|
2026-03-27 09:45:44 +00:00
|
|
|
f"- Contain ONLY the generated summary text in {to_language}.\n"
|
|
|
|
|
f"- Never contain inappropriate (hateful, sexual, violent) content. It is preferable to return no text than to generate such content.\n"
|
2026-03-25 21:10:10 +00:00
|
|
|
f"- Speak directly to the reader/listener, adopting the tone and style of a semi-formal news reporter or podcaster.\n"
|
|
|
|
|
f"- Where appropriate (fluency level, content), use a small number of idiomatic expressions.\n"
|
2026-03-27 09:45:44 +00:00
|
|
|
f"- Where appropriate use at least one additional tense, beyond the default of the content.\n"
|
|
|
|
|
f"- Be formatted in markdown. Contain a single level 1 header (#) at the top, followed by paragraphs and line breaks.\n"
|
|
|
|
|
f"- Be around {length_preference} long.\n"
|
|
|
|
|
f"- Be inspired by the content, but not the tone, of the source material."
|
2026-03-25 21:10:10 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def _create_prompt_summarise_text(
|
|
|
|
|
self,
|
|
|
|
|
source_material: str,
|
|
|
|
|
) -> str:
|
|
|
|
|
return (
|
|
|
|
|
f"Source material follows: \n\n"
|
|
|
|
|
f"{source_material}"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
async def generate_summary_text(
|
|
|
|
|
self,
|
|
|
|
|
content_to_summarise: str,
|
|
|
|
|
complexity_level: str,
|
|
|
|
|
from_language: str,
|
|
|
|
|
to_language: str,
|
|
|
|
|
length_preference="200-400 words") -> str:
|
|
|
|
|
"""Generate text using Anthropic."""
|
|
|
|
|
def _call() -> str:
|
|
|
|
|
message = self._client.messages.create(
|
|
|
|
|
model="claude-sonnet-4-6",
|
|
|
|
|
max_tokens=1024,
|
|
|
|
|
system=self._create_summarise_text_system_prompt(
|
|
|
|
|
complexity_level=complexity_level,
|
|
|
|
|
from_language=from_language,
|
|
|
|
|
to_language=to_language,
|
|
|
|
|
length_preference=length_preference,
|
|
|
|
|
),
|
|
|
|
|
messages=[
|
|
|
|
|
{
|
|
|
|
|
"role": "user",
|
|
|
|
|
"content": self._create_prompt_summarise_text(
|
|
|
|
|
content_to_summarise
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
return message.content[0].text
|
|
|
|
|
|
|
|
|
|
return await asyncio.to_thread(_call)
|