Skip to content

Commit 83be345

Browse files
authored
Change behaviour to behavior (#76)
1 parent 4595a71 commit 83be345

File tree

13 files changed

+38
-38
lines changed

13 files changed

+38
-38
lines changed

docs/agents.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -444,12 +444,12 @@ user_id=123 message='Hello John, would you be free for coffee sometime next week
444444

445445
## Model errors
446446

447-
If models behave unexpectedly (e.g., the retry limit is exceeded, or their API returns `503`), agent runs will raise [`UnexpectedModelBehaviour`][pydantic_ai.exceptions.UnexpectedModelBehaviour].
447+
If models behave unexpectedly (e.g., the retry limit is exceeded, or their API returns `503`), agent runs will raise [`UnexpectedModelBehavior`][pydantic_ai.exceptions.UnexpectedModelBehavior].
448448

449449
In these cases, [`agent.last_run_messages`][pydantic_ai.Agent.last_run_messages] can be used to access the messages exchanged during the run to help diagnose the issue.
450450

451451
```py
452-
from pydantic_ai import Agent, ModelRetry, UnexpectedModelBehaviour
452+
from pydantic_ai import Agent, ModelRetry, UnexpectedModelBehavior
453453

454454
agent = Agent('openai:gpt-4o')
455455

@@ -464,7 +464,7 @@ def calc_volume(size: int) -> int: # (1)!
464464

465465
try:
466466
result = agent.run_sync('Please get me the volume of a box with size 6.')
467-
except UnexpectedModelBehaviour as e:
467+
except UnexpectedModelBehavior as e:
468468
print('An error occurred:', e)
469469
#> An error occurred: Retriever exceeded max retries count of 1
470470
print('cause:', repr(e.__cause__))

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ async def main():
125125

126126
1. This [agent](agents.md) will act as first-tier support in a bank. Agents are generic in the type of dependencies they accept and the type of result they return. In this case, the support agent has type `#!python Agent[SupportDependencies, SupportResult]`.
127127
2. Here we configure the agent to use [OpenAI's GPT-4o model](api/models/openai.md), you can also set the model when running the agent.
128-
3. The `SupportDependencies` dataclass is used to pass data, connections, and logic into the model that will be needed when running [system prompt](agents.md#system-prompts) and [retriever](agents.md#retrievers) functions. PydanticAI's system of dependency injection provides a type-safe way to customise the behaviour of your agents, and can be especially useful when running unit tests and evals.
128+
3. The `SupportDependencies` dataclass is used to pass data, connections, and logic into the model that will be needed when running [system prompt](agents.md#system-prompts) and [retriever](agents.md#retrievers) functions. PydanticAI's system of dependency injection provides a type-safe way to customise the behavior of your agents, and can be especially useful when running unit tests and evals.
129129
4. Static [system prompts](agents.md#system-prompts) can be registered with the [`system_prompt` keyword argument][pydantic_ai.Agent.__init__] to the agent.
130130
5. Dynamic [system prompts](agents.md#system-prompts) can be registered with the [`@agent.system_prompt`][pydantic_ai.Agent.system_prompt] decorator, and can make use of dependency injection. Dependencies are carried via the [`CallContext`][pydantic_ai.dependencies.CallContext] argument, which is parameterized with the `deps_type` from above. If the type annotation here is wrong, static type checkers will catch it.
131131
6. [Retrievers](agents.md#retrievers) let you register "tools" which the LLM may call while responding to a user. Again, dependencies are carried via [`CallContext`][pydantic_ai.dependencies.CallContext], and any other arguments become the tool schema passed to the LLM. Pydantic is used to validate these arguments, and errors are passed back to the LLM so it can retry.

pydantic_ai/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from .agent import Agent
44
from .dependencies import CallContext
5-
from .exceptions import ModelRetry, UnexpectedModelBehaviour, UserError
5+
from .exceptions import ModelRetry, UnexpectedModelBehavior, UserError
66

7-
__all__ = 'Agent', 'CallContext', 'ModelRetry', 'UnexpectedModelBehaviour', 'UserError', '__version__'
7+
__all__ = 'Agent', 'CallContext', 'ModelRetry', 'UnexpectedModelBehavior', 'UserError', '__version__'
88
__version__ = version('pydantic_ai')

pydantic_ai/_retriever.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from . import _pydantic, _utils, messages
1212
from .dependencies import AgentDeps, CallContext, RetrieverContextFunc, RetrieverParams, RetrieverPlainFunc
13-
from .exceptions import ModelRetry, UnexpectedModelBehaviour
13+
from .exceptions import ModelRetry, UnexpectedModelBehavior
1414

1515
# Usage `RetrieverEitherFunc[AgentDependencies, P]`
1616
RetrieverEitherFunc = _utils.Either[
@@ -101,7 +101,7 @@ def _on_error(self, exc: ValidationError | ModelRetry, call_message: messages.To
101101
self._current_retry += 1
102102
if self._current_retry > self.max_retries:
103103
# TODO custom error with details of the retriever
104-
raise UnexpectedModelBehaviour(f'Retriever exceeded max retries count of {self.max_retries}') from exc
104+
raise UnexpectedModelBehavior(f'Retriever exceeded max retries count of {self.max_retries}') from exc
105105
else:
106106
if isinstance(exc, ValidationError):
107107
content = exc.errors(include_url=False)

pydantic_ai/agent.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ async def _handle_model_response(
660660
return _MarkFinalResult(result_data)
661661

662662
if not model_response.calls:
663-
raise exceptions.UnexpectedModelBehaviour('Received empty tool call message')
663+
raise exceptions.UnexpectedModelBehavior('Received empty tool call message')
664664

665665
# otherwise we run all retriever functions in parallel
666666
messages: list[_messages.Message] = []
@@ -723,7 +723,7 @@ async def _handle_streamed_model_response(
723723
pass
724724
structured_msg = model_response.get()
725725
if not structured_msg.calls:
726-
raise exceptions.UnexpectedModelBehaviour('Received empty tool call message')
726+
raise exceptions.UnexpectedModelBehavior('Received empty tool call message')
727727
messages: list[_messages.Message] = [structured_msg]
728728

729729
# we now run all retriever functions in parallel
@@ -748,7 +748,7 @@ async def _validate_result(
748748
def _incr_result_retry(self) -> None:
749749
self._current_result_retry += 1
750750
if self._current_result_retry > self._max_result_retries:
751-
raise exceptions.UnexpectedModelBehaviour(
751+
raise exceptions.UnexpectedModelBehavior(
752752
f'Exceeded maximum retries ({self._max_result_retries}) for result validation'
753753
)
754754

pydantic_ai/exceptions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import json
44

5-
__all__ = 'ModelRetry', 'UserError', 'UnexpectedModelBehaviour'
5+
__all__ = 'ModelRetry', 'UserError', 'UnexpectedModelBehavior'
66

77

88
class ModelRetry(Exception):
@@ -30,7 +30,7 @@ def __init__(self, message: str):
3030
super().__init__(message)
3131

3232

33-
class UnexpectedModelBehaviour(RuntimeError):
33+
class UnexpectedModelBehavior(RuntimeError):
3434
"""Error caused by unexpected Model behavior, e.g. an unexpected response code."""
3535

3636
message: str

pydantic_ai/models/gemini.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
from pydantic import Discriminator, Field, Tag
3737
from typing_extensions import NotRequired, TypedDict, TypeGuard, assert_never
3838

39-
from .. import UnexpectedModelBehaviour, _pydantic, _utils, exceptions, result
39+
from .. import UnexpectedModelBehavior, _pydantic, _utils, exceptions, result
4040
from ..messages import (
4141
ArgsObject,
4242
Message,
@@ -192,7 +192,7 @@ async def _make_request(self, messages: list[Message], streamed: bool) -> AsyncI
192192
async with self.http_client.stream('POST', url, content=request_json, headers=headers) as r:
193193
if r.status_code != 200:
194194
await r.aread()
195-
raise exceptions.UnexpectedModelBehaviour(f'Unexpected response from gemini {r.status_code}', r.text)
195+
raise exceptions.UnexpectedModelBehavior(f'Unexpected response from gemini {r.status_code}', r.text)
196196
yield r
197197

198198
@staticmethod
@@ -223,7 +223,7 @@ async def _process_streamed_response(http_response: HTTPResponse) -> EitherStrea
223223
break
224224

225225
if start_response is None:
226-
raise UnexpectedModelBehaviour('Streamed response ended without content or tool calls')
226+
raise UnexpectedModelBehavior('Streamed response ended without content or tool calls')
227227

228228
if _extract_response_parts(start_response).is_left():
229229
return GeminiStreamStructuredResponse(_content=content, _stream=aiter_bytes)
@@ -287,7 +287,7 @@ def get(self, *, final: bool = False) -> Iterable[str]:
287287
for part in parts:
288288
yield part['text']
289289
else:
290-
raise UnexpectedModelBehaviour(
290+
raise UnexpectedModelBehavior(
291291
'Streamed response with unexpected content, expected all parts to be text'
292292
)
293293

@@ -334,7 +334,7 @@ def get(self, *, final: bool = False) -> ModelStructuredResponse:
334334
combined_parts.extend(parts)
335335
elif not candidate.get('finish_reason'):
336336
# you can get an empty text part along with the finish_reason, so we ignore that case
337-
raise UnexpectedModelBehaviour(
337+
raise UnexpectedModelBehavior(
338338
'Streamed response with unexpected content, expected all parts to be function calls'
339339
)
340340
return _structured_response_from_parts(combined_parts, timestamp=self._timestamp)
@@ -534,14 +534,14 @@ def _extract_response_parts(
534534
Returns Either a list of function calls (Either.left) or a list of text parts (Either.right).
535535
"""
536536
if len(response['candidates']) != 1:
537-
raise UnexpectedModelBehaviour('Expected exactly one candidate in Gemini response')
537+
raise UnexpectedModelBehavior('Expected exactly one candidate in Gemini response')
538538
parts = response['candidates'][0]['content']['parts']
539539
if _all_function_call_parts(parts):
540540
return _utils.Either(left=parts)
541541
elif _all_text_parts(parts):
542542
return _utils.Either(right=parts)
543543
else:
544-
raise exceptions.UnexpectedModelBehaviour(
544+
raise exceptions.UnexpectedModelBehavior(
545545
f'Unsupported response from Gemini, expected all parts to be function calls or text, got: {parts!r}'
546546
)
547547

pydantic_ai/models/openai.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from openai.types.chat.chat_completion_chunk import ChoiceDeltaToolCall
1414
from typing_extensions import assert_never
1515

16-
from .. import UnexpectedModelBehaviour, _utils, result
16+
from .. import UnexpectedModelBehavior, _utils, result
1717
from ..messages import (
1818
ArgsJson,
1919
Message,
@@ -184,7 +184,7 @@ async def _process_streamed_response(response: AsyncStream[ChatCompletionChunk])
184184
try:
185185
first_chunk = await response.__anext__()
186186
except StopAsyncIteration as e: # pragma: no cover
187-
raise UnexpectedModelBehaviour('Streamed response ended without content or tool calls') from e
187+
raise UnexpectedModelBehavior('Streamed response ended without content or tool calls') from e
188188
timestamp = datetime.fromtimestamp(first_chunk.created, tz=timezone.utc)
189189
delta = first_chunk.choices[0].delta
190190
start_cost = _map_cost(first_chunk)
@@ -194,7 +194,7 @@ async def _process_streamed_response(response: AsyncStream[ChatCompletionChunk])
194194
try:
195195
next_chunk = await response.__anext__()
196196
except StopAsyncIteration as e:
197-
raise UnexpectedModelBehaviour('Streamed response ended without content or tool calls') from e
197+
raise UnexpectedModelBehavior('Streamed response ended without content or tool calls') from e
198198
delta = next_chunk.choices[0].delta
199199
start_cost += _map_cost(next_chunk)
200200

pydantic_ai/result.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ async def validate_structured_result(
279279
assert self._result_schema is not None, 'Expected _result_schema to not be None'
280280
match = self._result_schema.find_tool(message)
281281
if match is None:
282-
raise exceptions.UnexpectedModelBehaviour(
282+
raise exceptions.UnexpectedModelBehavior(
283283
f'Invalid message, unable to find tool: {self._result_schema.tool_names()}'
284284
)
285285

tests/models/test_gemini.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from pydantic import BaseModel
1313
from typing_extensions import Literal, TypeAlias
1414

15-
from pydantic_ai import Agent, ModelRetry, UnexpectedModelBehaviour, UserError, _utils
15+
from pydantic_ai import Agent, ModelRetry, UnexpectedModelBehavior, UserError, _utils
1616
from pydantic_ai.messages import (
1717
ArgsObject,
1818
ModelStructuredResponse,
@@ -511,7 +511,7 @@ def handler(_: httpx.Request):
511511
m = GeminiModel('gemini-1.5-flash', http_client=gemini_client)
512512
agent = Agent(m, system_prompt='this is the system prompt')
513513

514-
with pytest.raises(UnexpectedModelBehaviour) as exc_info:
514+
with pytest.raises(UnexpectedModelBehavior) as exc_info:
515515
await agent.run('Hello')
516516

517517
assert str(exc_info.value) == snapshot('Unexpected response from gemini 401, body:\ninvalid request')
@@ -535,7 +535,7 @@ async def test_heterogeneous_responses(get_gemini_client: GetGeminiClient):
535535
gemini_client = get_gemini_client(response)
536536
m = GeminiModel('gemini-1.5-flash', http_client=gemini_client)
537537
agent = Agent(m)
538-
with pytest.raises(UnexpectedModelBehaviour) as exc_info:
538+
with pytest.raises(UnexpectedModelBehavior) as exc_info:
539539
await agent.run('Hello')
540540

541541
assert str(exc_info.value) == snapshot(
@@ -573,7 +573,7 @@ async def test_stream_text_no_data(get_gemini_client: GetGeminiClient):
573573
gemini_client = get_gemini_client(stream)
574574
m = GeminiModel('gemini-1.5-flash', http_client=gemini_client)
575575
agent = Agent(m)
576-
with pytest.raises(UnexpectedModelBehaviour, match='Streamed response ended without con'):
576+
with pytest.raises(UnexpectedModelBehavior, match='Streamed response ended without con'):
577577
async with agent.run_stream('Hello'):
578578
pass
579579

@@ -691,5 +691,5 @@ async def test_stream_text_heterogeneous(get_gemini_client: GetGeminiClient):
691691

692692
msg = 'Streamed response with unexpected content, expected all parts to be text'
693693
async with agent.run_stream('Hello') as result:
694-
with pytest.raises(UnexpectedModelBehaviour, match=msg):
694+
with pytest.raises(UnexpectedModelBehavior, match=msg):
695695
await result.get_data()

0 commit comments

Comments
 (0)