Releases: jackmpcollins/magentic
v0.34.1
What's Changed
- Consume LLM output stream via returned objects to allow caching by @jackmpcollins in #384
- Improve ruff format/lint rules by @jackmpcollins in #385
- Update overview and configuration docs by @jackmpcollins in #386
Full Changelog: v0.34.0...v0.34.1
v0.34.0
What's Changed
Add StreamedResponse
and AsyncStreamedResponse
to enable parsing responses that contain both text and tool calls. See PR #383 or the new docs (copied below) https://magentic.dev/streaming/#StreamedResponse for more details.
⚡ StreamedResponse
Some LLMs have the ability to generate text output and make tool calls in the same response. This allows them to perform chain-of-thought reasoning or provide additional context to the user. In magentic, the StreamedResponse
(or AsyncStreamedResponse
) class can be used to request this type of output. This object is an iterable of StreamedStr
(or AsyncStreamedStr
) and FunctionCall
instances.
!!! warning "Consuming StreamedStr"
The StreamedStr object must be iterated over before the next item in the `StreamedResponse` is processed, otherwise the string output will be lost. This is because the `StreamedResponse` and `StreamedStr` share the same underlying generator, so advancing the `StreamedResponse` iterator skips over the `StreamedStr` items. The `StreamedStr` object has internal caching so after iterating over it once the chunks will remain available.
In the example below, we request that the LLM generates a greeting and then calls a function to get the weather for two cities. The StreamedResponse
object is then iterated over to print the output, and the StreamedStr
and FunctionCall
items are processed separately.
from magentic import prompt, FunctionCall, StreamedResponse, StreamedStr
def get_weather(city: str) -> str:
return f"The weather in {city} is 20°C."
@prompt(
"Say hello, then get the weather for: {cities}",
functions=[get_weather],
)
def describe_weather(cities: list[str]) -> StreamedResponse: ...
response = describe_weather(["Cape Town", "San Francisco"])
for item in response:
if isinstance(item, StreamedStr):
for chunk in item:
# print the chunks as they are received
print(chunk, sep="", end="")
print()
if isinstance(item, FunctionCall):
# print the function call, then call it and print the result
print(item)
print(item())
# Hello! I'll get the weather for Cape Town and San Francisco for you.
# FunctionCall(<function get_weather at 0x1109825c0>, 'Cape Town')
# The weather in Cape Town is 20°C.
# FunctionCall(<function get_weather at 0x1109825c0>, 'San Francisco')
# The weather in San Francisco is 20°C.
PRs
- Test Ollama via
OpenaiChatModel
by @jackmpcollins in #281 - Rename test to test_openai_chat_model_acomplete_ollama by @jackmpcollins in #381
- Add
(Async)StreamedResponse
for multi-part responses by @jackmpcollins in #383
Full Changelog: v0.33.0...v0.34.0
v0.33.0
What's Changed
Warning
Breaking change: The prompt-function return type and the output_types
argument to ChatModel
must now contain FunctionCall
or (Async)ParallelFunctionCall
if these return types are desired. Previously instances of these types could be returned even if they were not indicated in the output types.
- Dependency updates
- Improve development workflows
- Big internal refactor to prepare for future features. See PR #380 for details.
PRs
- Bump logfire-api from 0.49.0 to 0.52.0 by @dependabot in #327
- Bump litellm from 1.41.21 to 1.44.27 by @dependabot in #330
- Bump jupyterlab from 4.2.3 to 4.2.5 by @dependabot in #322
- Bump anthropic from 0.31.0 to 0.34.2 by @dependabot in #328
- Bump pydantic-settings from 2.3.4 to 2.5.2 by @dependabot in #332
- Bump notebook from 7.2.1 to 7.2.2 by @dependabot in #333
- Bump ruff from 0.5.2 to 0.6.5 by @dependabot in #331
- Bump jupyter from 1.0.0 to 1.1.1 by @dependabot in #335
- Bump logfire-api from 0.52.0 to 0.53.0 by @dependabot in #336
- Bump mkdocs-jupyter from 0.24.8 to 0.25.0 by @dependabot in #338
- Bump pytest-asyncio from 0.23.7 to 0.24.0 by @dependabot in #337
- Update precommit hooks by @jackmpcollins in #339
- Switch to uv from poetry by @jackmpcollins in #373
- Bump astral-sh/setup-uv from 2 to 3 by @dependabot in #374
- Use VCR for tests by @jackmpcollins in #375
- Add CONTRIBUTING.md by @jackmpcollins in #376
- Make VCR match on request body in tests by @jackmpcollins in #377
- Add make help command by @jackmpcollins in #378
- Bump astral-sh/setup-uv from 3 to 4 by @dependabot in #379
- Refactor to reuse stream parsing across ChatModels by @jackmpcollins in #380
Full Changelog: v0.32.0...v0.33.0
v0.32.0
What's Changed
Add support for OpenAI "strict" setting for structured outputs. This guarantees that the generated JSON schema matches that supplied by the user. In magentic, this is set via an extension of pydantic's ConfigDict
, and works for pydantic models as well as functions. See the docs for more info https://magentic.dev/structured-outputs/#configdict
For a BaseModel
from magentic import prompt, ConfigDict
from pydantic import BaseModel
class Superhero(BaseModel):
model_config = ConfigDict(openai_strict=True)
name: str
age: int
power: str
enemies: list[str]
@prompt("Create a Superhero named {name}.")
def create_superhero(name: str) -> Superhero: ...
create_superhero("Garden Man")
For a function
from typing import Annotated, Literal
from magentic import ConfigDict, with_config
from pydantic import Field
@with_config(ConfigDict(openai_strict=True))
def activate_oven(
temperature: Annotated[int, Field(description="Temp in Fahrenheit", lt=500)],
mode: Literal["broil", "bake", "roast"],
) -> str:
"""Turn the oven on with the provided settings."""
return f"Preheating to {temperature} F with mode {mode}"
@prompt(
"Do some cooking",
functions=[
activate_oven,
# ...
PRs
- Add support for OpenAI structured outputs by @jackmpcollins in #305
Full Changelog: v0.31.0...v0.32.0
v0.31.0
v0.30.0
What's Changed
Warning
Breaking change: StructuredOutputError
has been replaced by more specific exceptions StringNotAllowedError
and ToolSchemaParseError
in PR #288
🤖 ♻️ LLM-Assisted retries has been added. When enabled, this sends incorrectly formatted output back to the LLM along with the error message to have the LLM fix its mistakes. This can be used to enforce more complex validation on output schemas using pydantic validators.
For example, placing an arbitrary constraint on a string field
from typing import Annotated
from magentic import prompt
from pydantic import AfterValidator, BaseModel
def assert_is_ireland(v: str) -> str:
if v != "Ireland":
raise ValueError("Country must be Ireland")
return v
class Country(BaseModel):
name: Annotated[str, AfterValidator(assert_is_ireland)]
capital: str
@prompt(
"Return a country",
max_retries=3,
)
def get_country() -> Country: ...
get_country()
# 05:13:55.607 Calling prompt-function get_country
# 05:13:55.622 LLM-assisted retries enabled. Max 3
# 05:13:55.627 Chat Completion with 'gpt-4o' [LLM]
# 05:13:56.309 streaming response from 'gpt-4o' took 0.11s [LLM]
# 05:13:56.310 Retrying Chat Completion. Attempt 1.
# 05:13:56.322 Chat Completion with 'gpt-4o' [LLM]
# 05:13:57.456 streaming response from 'gpt-4o' took 0.00s [LLM]
#
# Country(name='Ireland', capital='Dublin')
See the new docs page on Retrying for more info.
PRs
- Bump aiohttp from 3.9.5 to 3.10.2 by @dependabot in #297
- Add LLM-assisted retries by @jackmpcollins in #288
- Set logfire OTEL scope to magentic by @jackmpcollins in #298
Full Changelog: v0.29.0...v0.30.0
v0.29.0
What's Changed
- Make Message a pydantic model / serializable by @jackmpcollins in #294
This means Message
objects can be used anywhere pydantic models can, including in prompt-functions. The new AnyMessage
type simplifies this. For example
from magentic import AnyMessage, prompt
@prompt("Create an example of few-shot prompting for a chatbot")
def make_few_shot_prompt() -> list[AnyMessage]: ...
make_few_shot_prompt()
# [SystemMessage('You are a helpful and knowledgeable assistant.'),
# UserMessage('What’s the weather like today?'),
# AssistantMessage[Any]('The weather today is sunny with a high of 75°F (24°C).'),
# UserMessage('Can you explain the theory of relativity in simple terms?'),
# AssistantMessage[Any]('Sure! The theory of relativity, developed by Albert Einstein, ...]
Dependabot
- Bump logfire-api from 0.46.1 to 0.49.0 by @dependabot in #292
- Bump logfire from 0.46.1 to 0.49.0 by @dependabot in #293
- Bump pytest from 8.2.2 to 8.3.2 by @dependabot in #286
- Bump openai from 1.35.13 to 1.38.0 by @dependabot in #290
- Bump mypy from 1.10.1 to 1.11.1 by @dependabot in #291
Full Changelog: v0.28.1...v0.29.0
v0.28.1
What's Changed
- Bump ruff from 0.4.10 to 0.5.2 by @dependabot in #269
- Limit tool call id to 9 chars for Mistral by @jackmpcollins in #279
Full Changelog: v0.28.0...v0.28.1
v0.28.0
What's Changed
🪵 🔥 Logfire / OpenTelemetry now supported!
This makes it much easier to follow what tool calls are being made by the LLM both as printed output locally and in Logfire or another monitoring service. It also lets you see the raw requests being sent to OpenAI/Anthropic so you can more easily debug issues.
All it takes to get set up is
pip install logfire
import logfire
logfire.configure(send_to_logfire=False) # Or True to use the Logfire service
logfire.instrument_openai() # optional, to trace OpenAI API calls
# logfire.instrument_anthropic() # optional, to trace Anthropic API calls
Check out the new docs page: https://magentic.dev/logging-and-tracing/
PRs
Add basic logging and MAGENTIC_VERBOSE env var by @jackmpcollins in #263- Update dependencies by @jackmpcollins in #264
- Instrument for Logfire / OpenTelemetry by @jackmpcollins in #265
- Do not set stream_options when using AzureOpenAI by @jackmpcollins in #262
- Use new
parallel_tool_calls
arg with OpenAI API by @jackmpcollins in #267 - Fix LitellmChatModel tool_choice parameter to force Anthropic tool use by @jackmpcollins in #268
Full Changelog: v0.27.0...v0.28.0
v0.27.0
What's Changed
- Add peek, apeek, adropwhile functions by @jackmpcollins in #229
- Update anthropic_chat_model.py to conform with latest anthropic package by @myousefi in #239
- Bump requests from 2.31.0 to 2.32.0 by @dependabot in #218
- Bump jinja2 from 3.1.3 to 3.1.4 by @dependabot in #203
- Bump urllib3 from 2.2.1 to 2.2.2 by @dependabot in #238
- Bump tornado from 6.4 to 6.4.1 by @dependabot in #233
New Contributors
Full Changelog: v0.26.0...v0.27.0