diff --git a/Makefile b/Makefile index b98bf4d7..11370df5 100644 --- a/Makefile +++ b/Makefile @@ -56,4 +56,4 @@ docs-serve: # Build and serve the documentation uv run mkdocs serve .PHONY: all -all: format lint test +all: format lint typecheck test diff --git a/src/magentic/chat_model/anthropic_chat_model.py b/src/magentic/chat_model/anthropic_chat_model.py index dc54d567..b4ce4e7d 100644 --- a/src/magentic/chat_model/anthropic_chat_model.py +++ b/src/magentic/chat_model/anthropic_chat_model.py @@ -1,7 +1,6 @@ import base64 import json from collections.abc import ( - AsyncIterable, AsyncIterator, Callable, Iterable, @@ -10,25 +9,23 @@ ) from enum import Enum from functools import singledispatch -from itertools import chain, groupby +from itertools import groupby from typing import Any, Generic, TypeVar, cast, overload import filetype -from pydantic import ValidationError from magentic.chat_model.base import ( ChatModel, - ToolSchemaParseError, - avalidate_str_content, - validate_str_content, + aparse_stream, + parse_stream, ) from magentic.chat_model.function_schema import ( - AsyncFunctionSchema, BaseFunctionSchema, FunctionCallFunctionSchema, FunctionSchema, - async_function_schema_for_type, function_schema_for_type, + get_async_function_schemas, + get_function_schemas, ) from magentic.chat_model.message import ( AssistantMessage, @@ -39,8 +36,14 @@ UserMessage, _RawMessage, ) +from magentic.chat_model.stream import ( + AsyncOutputStream, + FunctionCallChunk, + OutputStream, + StreamParser, + StreamState, +) from magentic.function_call import ( - AsyncParallelFunctionCall, FunctionCall, ParallelFunctionCall, _create_unique_id, @@ -48,28 +51,15 @@ from magentic.streaming import ( AsyncStreamedStr, StreamedStr, - aapply, - achain, - agroupby, - apeek, - apply, - async_iter, - peek, ) -from magentic.typing import is_any_origin_subclass, is_origin_subclass +from magentic.typing import is_any_origin_subclass from magentic.vision import UserImageMessage try: import anthropic from anthropic.lib.streaming import MessageStreamEvent from anthropic.lib.streaming._messages import accumulate_event - from anthropic.types import ( - ContentBlockDeltaEvent, - ContentBlockStartEvent, - MessageParam, - ToolParam, - ToolUseBlock, - ) + from anthropic.types import MessageParam, ToolParam from anthropic.types.message_create_params import ToolChoice except ImportError as error: msg = "To use AnthropicChatModel you must install the `anthropic` package using `pip install 'magentic[anthropic]'`." @@ -241,127 +231,59 @@ def as_tool_choice(self) -> ToolChoice: return {"type": "tool", "name": self._function_schema.name} -# TODO: Generalize this to BaseToolSchema when that is created -BeseToolSchemaT = TypeVar("BeseToolSchemaT", bound=BaseFunctionToolSchema[Any]) +class AnthropicStreamParser(StreamParser[MessageStreamEvent]): + def is_content(self, item: MessageStreamEvent) -> bool: + return item.type == "content_block_delta" + def get_content(self, item: MessageStreamEvent) -> str | None: + if item.type == "text": + return item.text + return None -def select_tool_schema( - tool_call: ToolUseBlock, - tool_schemas: Iterable[BeseToolSchemaT], -) -> BeseToolSchemaT: - """Select the tool schema based on the response chunk.""" - for tool_schema in tool_schemas: - if tool_schema._function_schema.name == tool_call.name: - return tool_schema + def is_tool_call(self, item: MessageStreamEvent) -> bool: + return ( + item.type == "content_block_start" and item.content_block.type == "tool_use" + ) - msg = f"Unknown tool call: {tool_call.model_dump_json()}" - raise ValueError(msg) + def iter_tool_calls(self, item: MessageStreamEvent) -> Iterable[FunctionCallChunk]: + if item.type == "content_block_start" and item.content_block.type == "tool_use": + return [ + FunctionCallChunk( + id=item.content_block.id, name=item.content_block.name, args=None + ) + ] + if item.type == "input_json": + return [FunctionCallChunk(id=None, name=None, args=item.partial_json)] + return [] -class FunctionToolSchema(BaseFunctionToolSchema[FunctionSchema[T]]): - def parse_tool_call(self, chunks: Iterable[MessageStreamEvent]) -> T: - return self._function_schema.parse_args( - chunk.delta.partial_json - for chunk in chunks - if chunk.type == "content_block_delta" - if chunk.delta.type == "input_json_delta" +class AnthropicStreamState(StreamState[MessageStreamEvent]): + def __init__(self) -> None: + self._current_message_snapshot: anthropic.types.Message | None = ( + None # TODO: type ) + self.usage_ref: list[Usage] = [] - -class AsyncFunctionToolSchema(BaseFunctionToolSchema[AsyncFunctionSchema[T]]): - async def aparse_tool_call(self, chunks: AsyncIterable[MessageStreamEvent]) -> T: - return await self._function_schema.aparse_args( - chunk.delta.partial_json - async for chunk in chunks - if chunk.type == "content_block_delta" - if chunk.delta.type == "input_json_delta" + def update(self, item: MessageStreamEvent) -> None: + self._current_message_snapshot = accumulate_event( + # Unrecognized event types are ignored + event=item, # type: ignore[arg-type] + current_snapshot=self._current_message_snapshot, ) + if item.type == "message_stop": + assert not self.usage_ref # noqa: S101 + self.usage_ref.append( + Usage( + input_tokens=item.message.usage.input_tokens, + output_tokens=item.message.usage.output_tokens, + ) + ) - -def _iter_streamed_tool_calls( - response: Iterable[MessageStreamEvent], -) -> Iterator[Iterator[ContentBlockStartEvent | ContentBlockDeltaEvent]]: - all_tool_call_chunks = ( - cast(ContentBlockStartEvent | ContentBlockDeltaEvent, chunk) - for chunk in response - if chunk.type in ("content_block_start", "content_block_delta") - ) - for _, tool_call_chunks in groupby(all_tool_call_chunks, lambda x: x.index): - yield tool_call_chunks - - -async def _aiter_streamed_tool_calls( - response: AsyncIterable[MessageStreamEvent], -) -> AsyncIterator[AsyncIterator[ContentBlockStartEvent | ContentBlockDeltaEvent]]: - all_tool_call_chunks = ( - cast(ContentBlockStartEvent | ContentBlockDeltaEvent, chunk) - async for chunk in response - if chunk.type in ("content_block_start", "content_block_delta") - ) - async for _, tool_call_chunks in agroupby(all_tool_call_chunks, lambda x: x.index): - yield tool_call_chunks - - -def _join_streamed_response_to_message( - response: list[MessageStreamEvent], -) -> _RawMessage[MessageParam]: - snapshot = None - for event in response: - snapshot = accumulate_event( - event=event, # type: ignore[arg-type] - current_snapshot=snapshot, - ) - assert snapshot is not None # noqa: S101 - snapshot_content = snapshot.model_dump()["content"] - return _RawMessage({"role": snapshot.role, "content": snapshot_content}) - - -def _parse_streamed_tool_calls( - response: Iterable[MessageStreamEvent], - tool_schemas: Iterable[FunctionToolSchema[T]], -) -> Iterator[T]: - cached_response: list[MessageStreamEvent] = [] - response = apply(cached_response.append, response) - try: - for tool_call_chunks in _iter_streamed_tool_calls(response): - first_chunk, tool_call_chunks = peek(tool_call_chunks) - assert first_chunk.type == "content_block_start" # noqa: S101 - assert first_chunk.content_block.type == "tool_use" # noqa: S101 - tool_schema = select_tool_schema(first_chunk.content_block, tool_schemas) - tool_call = tool_schema.parse_tool_call(tool_call_chunks) - yield tool_call - # TODO: Catch/raise unknown tool call error here - except ValidationError as e: - raw_message = _join_streamed_response_to_message(cached_response) - raise ToolSchemaParseError( - output_message=raw_message, - tool_call_id=raw_message.content["content"][0]["id"], # type: ignore[index,unused-ignore] - validation_error=e, - ) from e - - -async def _aparse_streamed_tool_calls( - response: AsyncIterable[MessageStreamEvent], - tool_schemas: Iterable[AsyncFunctionToolSchema[T]], -) -> AsyncIterator[T]: - cached_response: list[MessageStreamEvent] = [] - response = aapply(cached_response.append, response) - try: - async for tool_call_chunks in _aiter_streamed_tool_calls(response): - first_chunk, tool_call_chunks = await apeek(tool_call_chunks) - assert first_chunk.type == "content_block_start" # noqa: S101 - assert first_chunk.content_block.type == "tool_use" # noqa: S101 - tool_schema = select_tool_schema(first_chunk.content_block, tool_schemas) - tool_call = await tool_schema.aparse_tool_call(tool_call_chunks) - yield tool_call - # TODO: Catch/raise unknown tool call error here - except ValidationError as e: - raw_message = _join_streamed_response_to_message(cached_response) - raise ToolSchemaParseError( - output_message=raw_message, - tool_call_id=raw_message.content["content"][0]["id"], # type: ignore[index,unused-ignore] - validation_error=e, - ) from e + @property + def current_message_snapshot(self) -> Message[Any]: + assert self._current_message_snapshot is not None # noqa: S101 + # TODO: Possible to return AssistantMessage here? + return _RawMessage(self._current_message_snapshot.model_dump()) def _extract_system_message( @@ -377,75 +299,13 @@ def _extract_system_message( ) -def _create_usage_ref( - response: Iterable[MessageStreamEvent], -) -> tuple[list[Usage], Iterator[MessageStreamEvent]]: - """Returns a pointer to a Usage object that is created at the end of the response.""" - usage_ref: list[Usage] = [] - - def generator( - response: Iterable[MessageStreamEvent], - ) -> Iterator[MessageStreamEvent]: - message_start_usage = None - output_tokens = None - for chunk in response: - if chunk.type == "message_start": - message_start_usage = chunk.message.usage - if chunk.type == "message_delta": - output_tokens = chunk.usage.output_tokens - yield chunk - if message_start_usage and output_tokens: - usage_ref.append( - Usage( - input_tokens=message_start_usage.input_tokens, - output_tokens=message_start_usage.output_tokens + output_tokens, - ) - ) - - return usage_ref, generator(response) - - -def _create_usage_ref_async( - response: AsyncIterable[MessageStreamEvent], -) -> tuple[list[Usage], AsyncIterator[MessageStreamEvent]]: - """Async version of `_create_usage_ref`.""" - usage_ref: list[Usage] = [] - - async def agenerator( - response: AsyncIterable[MessageStreamEvent], - ) -> AsyncIterator[MessageStreamEvent]: - message_start_usage = None - output_tokens = None - async for chunk in response: - if chunk.type == "message_start": - message_start_usage = chunk.message.usage - if chunk.type == "message_delta": - output_tokens = chunk.usage.output_tokens - yield chunk - if message_start_usage and output_tokens: - usage_ref.append( - Usage( - input_tokens=message_start_usage.input_tokens, - output_tokens=message_start_usage.output_tokens + output_tokens, - ) - ) - - return usage_ref, agenerator(response) +def _if_given(value: T | None) -> T | anthropic.NotGiven: + return value if value is not None else anthropic.NOT_GIVEN R = TypeVar("R") -STR_OR_FUNCTIONCALL_TYPE = ( - str, - StreamedStr, - AsyncStreamedStr, - FunctionCall, - ParallelFunctionCall, - AsyncParallelFunctionCall, -) - - class AnthropicChatModel(ChatModel): """An LLM chat model that uses the `anthropic` python package.""" @@ -534,84 +394,36 @@ def complete( if output_types is None: output_types = [] if functions else cast(list[type[R]], [str]) - # TODO: Check that Function calls types match functions - function_schemas = [FunctionCallFunctionSchema(f) for f in functions or []] + [ - function_schema_for_type(type_) - for type_ in output_types - if not is_origin_subclass(type_, STR_OR_FUNCTIONCALL_TYPE) - ] - tool_schemas = [FunctionToolSchema(schema) for schema in function_schemas] + function_schemas = get_function_schemas(functions, output_types) + tool_schemas = [BaseFunctionToolSchema(schema) for schema in function_schemas] - str_in_output_types = is_any_origin_subclass(output_types, str) - streamed_str_in_output_types = is_any_origin_subclass(output_types, StreamedStr) - allow_string_output = str_in_output_types or streamed_str_in_output_types + allow_string_output = is_any_origin_subclass(output_types, (str, StreamedStr)) system, messages = _extract_system_message(messages) - def _response_generator() -> Iterator[MessageStreamEvent]: - with self._client.messages.stream( - model=self.model, - messages=_combine_messages( - [message_to_anthropic_message(m) for m in messages] - ), - max_tokens=self.max_tokens, - stop_sequences=stop or anthropic.NOT_GIVEN, - system=system, - temperature=( - self.temperature - if self.temperature is not None - else anthropic.NOT_GIVEN - ), - tools=( - [schema.to_dict() for schema in tool_schemas] or anthropic.NOT_GIVEN - ), - tool_choice=self._get_tool_choice( - tool_schemas=tool_schemas, allow_string_output=allow_string_output - ), - ) as stream: - yield from stream - - response = _response_generator() - usage_ref, response = _create_usage_ref(response) - - message_start_chunk = next(response) - assert message_start_chunk.type == "message_start" # noqa: S101 - first_chunk = next(response) - assert first_chunk.type == "content_block_start" # noqa: S101 - response = chain([message_start_chunk, first_chunk], response) - - if ( - first_chunk.type == "content_block_start" - and first_chunk.content_block.type == "text" - ): - streamed_str = StreamedStr( - chunk.delta.text - for chunk in response - if chunk.type == "content_block_delta" - and chunk.delta.type == "text_delta" - ) - str_content = validate_str_content( - streamed_str, - allow_string_output=allow_string_output, - streamed=streamed_str_in_output_types, - ) - return AssistantMessage._with_usage(str_content, usage_ref) # type: ignore[return-value] - - if ( - first_chunk.type == "content_block_start" - and first_chunk.content_block.type == "tool_use" - ): - tool_calls = _parse_streamed_tool_calls(response, tool_schemas) - if is_any_origin_subclass(output_types, ParallelFunctionCall): - content = ParallelFunctionCall(tool_calls) - return AssistantMessage._with_usage(content, usage_ref) # type: ignore[return-value] - # Take only the first tool_call, silently ignore extra chunks - # TODO: Create generator here that raises error or warns if multiple tool_calls - content = next(tool_calls) - return AssistantMessage._with_usage(content, usage_ref) # type: ignore[return-value] - - msg = f"Could not determine response type for first chunk: {first_chunk.model_dump_json()}" - raise ValueError(msg) + response: Iterator[MessageStreamEvent] = self._client.messages.stream( + model=self.model, + messages=_combine_messages( + [message_to_anthropic_message(m) for m in messages] + ), + max_tokens=self.max_tokens, + stop_sequences=_if_given(stop), + system=system, + temperature=_if_given(self.temperature), + tools=[schema.to_dict() for schema in tool_schemas] or anthropic.NOT_GIVEN, + tool_choice=self._get_tool_choice( + tool_schemas=tool_schemas, allow_string_output=allow_string_output + ), + ).__enter__() + stream = OutputStream( + response, + function_schemas=function_schemas, + parser=AnthropicStreamParser(), + state=AnthropicStreamState(), + ) + return AssistantMessage._with_usage( + parse_stream(stream, output_types), usage_ref=stream.usage_ref + ) @overload async def acomplete( @@ -645,83 +457,37 @@ async def acomplete( if output_types is None: output_types = [] if functions else cast(list[type[R]], [str]) - function_schemas = [FunctionCallFunctionSchema(f) for f in functions or []] + [ - async_function_schema_for_type(type_) - for type_ in output_types - if not is_origin_subclass(type_, STR_OR_FUNCTIONCALL_TYPE) - ] - tool_schemas = [AsyncFunctionToolSchema(schema) for schema in function_schemas] + function_schemas = get_async_function_schemas(functions, output_types) + tool_schemas = [BaseFunctionToolSchema(schema) for schema in function_schemas] - str_in_output_types = is_any_origin_subclass(output_types, str) - async_streamed_str_in_output_types = is_any_origin_subclass( - output_types, AsyncStreamedStr + allow_string_output = is_any_origin_subclass( + output_types, (str, AsyncStreamedStr) ) - allow_string_output = str_in_output_types or async_streamed_str_in_output_types system, messages = _extract_system_message(messages) - async def _response_generator() -> AsyncIterator[MessageStreamEvent]: - async with self._async_client.messages.stream( - model=self.model, - messages=_combine_messages( - [message_to_anthropic_message(m) for m in messages] - ), - max_tokens=self.max_tokens, - stop_sequences=stop or anthropic.NOT_GIVEN, - system=system, - temperature=( - self.temperature - if self.temperature is not None - else anthropic.NOT_GIVEN - ), - tools=( - [schema.to_dict() for schema in tool_schemas] or anthropic.NOT_GIVEN - ), - tool_choice=self._get_tool_choice( - tool_schemas=tool_schemas, allow_string_output=allow_string_output - ), - ) as stream: - async for chunk in stream: - yield chunk - - response = _response_generator() - usage_ref, response = _create_usage_ref_async(response) - - message_start_chunk = await anext(response) - assert message_start_chunk.type == "message_start" # noqa: S101 - first_chunk = await anext(response) - assert first_chunk.type == "content_block_start" # noqa: S101 - response = achain(async_iter([message_start_chunk, first_chunk]), response) - - if ( - first_chunk.type == "content_block_start" - and first_chunk.content_block.type == "text" - ): - async_streamed_str = AsyncStreamedStr( - chunk.delta.text - async for chunk in response - if chunk.type == "content_block_delta" - and chunk.delta.type == "text_delta" - ) - str_content = await avalidate_str_content( - async_streamed_str, - allow_string_output=allow_string_output, - streamed=async_streamed_str_in_output_types, - ) - return AssistantMessage._with_usage(str_content, usage_ref) # type: ignore[return-value] - - if ( - first_chunk.type == "content_block_start" - and first_chunk.content_block.type == "tool_use" - ): - tool_calls = _aparse_streamed_tool_calls(response, tool_schemas) - if is_any_origin_subclass(output_types, AsyncParallelFunctionCall): - content = AsyncParallelFunctionCall(tool_calls) - return AssistantMessage._with_usage(content, usage_ref) # type: ignore[return-value] - # Take only the first tool_call, silently ignore extra chunks - # TODO: Create generator here that raises error or warns if multiple tool_calls - content = await anext(tool_calls) - return AssistantMessage._with_usage(content, usage_ref) # type: ignore[return-value] - - msg = "Could not determine response type" - raise ValueError(msg) + response: AsyncIterator[ + MessageStreamEvent + ] = await self._async_client.messages.stream( + model=self.model, + messages=_combine_messages( + [message_to_anthropic_message(m) for m in messages] + ), + max_tokens=self.max_tokens, + stop_sequences=_if_given(stop), + system=system, + temperature=_if_given(self.temperature), + tools=[schema.to_dict() for schema in tool_schemas] or anthropic.NOT_GIVEN, + tool_choice=self._get_tool_choice( + tool_schemas=tool_schemas, allow_string_output=allow_string_output + ), + ).__aenter__() + stream = AsyncOutputStream( + response, + function_schemas=function_schemas, + parser=AnthropicStreamParser(), + state=AnthropicStreamState(), + ) + return AssistantMessage._with_usage( + await aparse_stream(stream, output_types), usage_ref=stream.usage_ref + ) diff --git a/src/magentic/chat_model/base.py b/src/magentic/chat_model/base.py index 8998e76c..2575097b 100644 --- a/src/magentic/chat_model/base.py +++ b/src/magentic/chat_model/base.py @@ -2,12 +2,18 @@ from abc import ABC, abstractmethod from collections.abc import Callable, Iterable from contextvars import ContextVar -from typing import Any, TypeVar, overload +from itertools import chain +from typing import Any, AsyncIterator, Iterator, TypeVar, cast, get_origin, overload from pydantic import ValidationError from magentic.chat_model.message import AssistantMessage, Message -from magentic.streaming import AsyncStreamedStr, StreamedStr +from magentic.function_call import ( + AsyncParallelFunctionCall, + FunctionCall, + ParallelFunctionCall, +) +from magentic.streaming import AsyncStreamedStr, StreamedStr, achain, async_iter R = TypeVar("R") @@ -18,17 +24,59 @@ # TODO: Parent class with `output_message` attribute ? class StringNotAllowedError(Exception): - """Raised when a string is returned by the LLM but not expected.""" + """Raised when a string is returned by the LLM but not allowed.""" _MESSAGE = ( - "A string was returned by the LLM but was not an allowed output type." - ' Consider updating the prompt to encourage the LLM to "use the tool".' + "A string was returned by the LLM but is not an allowed output type." + " Consider updating the allowed output types or modifying the prompt." " Model output: {model_output!r}" ) - def __init__(self, output_message: Message[Any]): - super().__init__(self._MESSAGE.format(model_output=output_message.content)) + def __init__(self, model_output: str): + super().__init__(self._MESSAGE.format(model_output=model_output)) + self.output_message = AssistantMessage(model_output) + + +class FunctionCallNotAllowedError(Exception): + """Raised when a FunctionCall is returned by the LLM but not allowed.""" + + _MESSAGE = ( + "A function call was returned by the LLM but is not an allowed output type." + " Consider updating the allowed output types or modifying the prompt." + " FunctionCall: {function_call!r}" + ) + + def __init__(self, function_call: FunctionCall[Any]): + super().__init__(self._MESSAGE.format(function_call=function_call)) + self.output_message = AssistantMessage(function_call) + + +class ObjectNotAllowedError(Exception): + """Raised when a Python object is returned by the LLM but not allowed.""" + + _MESSAGE = ( + "An object was returned by the LLM but is not an allowed output type." + " Consider updating the allowed output types or modifying the prompt." + " Object: {obj!r}" + ) + + def __init__(self, obj: Any): + super().__init__(self._MESSAGE.format(obj=obj)) + self.output_message = AssistantMessage(obj) + + +class UnknownToolError(Exception): + """Raised when the LLM returns a tool call for an unknown tool.""" + + _MESSAGE = ( + "The LLM returned a tool call for a tool name that is not recognized." + " Tool name: {tool_name!r}" + ) + + def __init__(self, output_message: Message[Any], tool_call_id: str, tool_name: str): + super().__init__(self._MESSAGE.format(tool_name=tool_name)) self.output_message = output_message + self.tool_call_id = tool_call_id class ToolSchemaParseError(Exception): @@ -52,28 +100,53 @@ def __init__( self.validation_error = validation_error -def validate_str_content( - streamed_str: StreamedStr, *, allow_string_output: bool, streamed: bool -) -> StreamedStr | str: - """Raise error if string output not expected. Otherwise return correct string type.""" - if not allow_string_output: - model_output = streamed_str.truncate(100) - raise StringNotAllowedError(AssistantMessage(model_output)) - if streamed: - return streamed_str - return str(streamed_str) - - -async def avalidate_str_content( - async_streamed_str: AsyncStreamedStr, *, allow_string_output: bool, streamed: bool -) -> AsyncStreamedStr | str: - """Async version of `validate_str_content`.""" - if not allow_string_output: - model_output = await async_streamed_str.truncate(100) - raise StringNotAllowedError(AssistantMessage(model_output)) - if streamed: - return async_streamed_str - return await async_streamed_str.to_string() +# TODO: Make this a stream class with a close method and context management +def parse_stream(stream: Iterator[Any], output_types: Iterable[type[R]]) -> R: + """Parse and validate the LLM output stream against the allowed output types.""" + output_type_origins = [get_origin(type_) or type_ for type_ in output_types] + # TODO: option to error/warn/ignore extra objects + # TODO: warn for degenerate output types ? + obj = next(stream) + # TODO: Add type for mixed StreamedStr and FunctionCalls + if isinstance(obj, StreamedStr): + if StreamedStr in output_type_origins: + return cast(R, obj) + if str in output_type_origins: + return cast(R, str(obj)) + raise StringNotAllowedError(obj.truncate(100)) + if isinstance(obj, FunctionCall): + if ParallelFunctionCall in output_type_origins: + return cast(R, ParallelFunctionCall(chain([obj], stream))) + if FunctionCall in output_type_origins: + # TODO: Check that FunctionCall type matches ? + return cast(R, obj) + raise FunctionCallNotAllowedError(obj) + if isinstance(obj, tuple(output_type_origins)): + return cast(R, obj) + raise ObjectNotAllowedError(obj) + + +async def aparse_stream( + stream: AsyncIterator[Any], output_types: Iterable[type[R]] +) -> R: + """Async version of `parse_stream`.""" + output_type_origins = [get_origin(type_) or type_ for type_ in output_types] + obj = await anext(stream) + if isinstance(obj, AsyncStreamedStr): + if AsyncStreamedStr in output_type_origins: + return cast(R, obj) + if str in output_type_origins: + return cast(R, await obj.to_string()) + raise StringNotAllowedError(await obj.truncate(100)) + if isinstance(obj, FunctionCall): + if AsyncParallelFunctionCall in output_type_origins: + return cast(R, AsyncParallelFunctionCall(achain(async_iter([obj]), stream))) + if FunctionCall in output_type_origins: + return cast(R, obj) + raise FunctionCallNotAllowedError(obj) + if isinstance(obj, tuple(output_type_origins)): + return cast(R, obj) + raise ObjectNotAllowedError(obj) class ChatModel(ABC): @@ -106,6 +179,7 @@ def complete( self, messages: Iterable[Message[Any]], functions: Iterable[Callable[..., Any]] | None = None, + # TODO: Set default of R to str in Python 3.13 output_types: Iterable[type[R | str]] | None = None, *, stop: list[str] | None = None, diff --git a/src/magentic/chat_model/function_schema.py b/src/magentic/chat_model/function_schema.py index df74efc3..2d84c114 100644 --- a/src/magentic/chat_model/function_schema.py +++ b/src/magentic/chat_model/function_schema.py @@ -9,12 +9,18 @@ from pydantic import BaseModel, TypeAdapter, create_model from magentic._pydantic import ConfigDict, get_pydantic_config, json_schema -from magentic.function_call import FunctionCall +from magentic.function_call import ( + AsyncParallelFunctionCall, + FunctionCall, + ParallelFunctionCall, +) from magentic.streaming import ( + AsyncStreamedStr, + StreamedStr, aiter_streamed_json_array, iter_streamed_json_array, ) -from magentic.typing import is_origin_abstract, name_type +from magentic.typing import is_origin_abstract, is_origin_subclass, name_type T = TypeVar("T") @@ -59,6 +65,19 @@ def dict(self) -> FunctionDefinition: return schema +BaseFunctionSchemaT = TypeVar("BaseFunctionSchemaT", bound=BaseFunctionSchema[Any]) + + +def select_function_schema( + function_schemas: Iterable[BaseFunctionSchemaT], name: str +) -> BaseFunctionSchemaT | None: + """Select the function schema with the given name.""" + for schema in function_schemas: + if schema.name == name: + return schema + return None + + class AsyncFunctionSchema(BaseFunctionSchema[T], Generic[T]): @abstractmethod async def aparse_args(self, chunks: AsyncIterable[str]) -> T: @@ -431,3 +450,43 @@ def serialize_args(self, value: FunctionCall[T]) -> str: return self._model.model_construct(**value.arguments).model_dump_json( exclude_unset=True ) + + +R = TypeVar("R") + +_NON_FUNCTION_CALL_TYPES = ( + str, + StreamedStr, + AsyncStreamedStr, + FunctionCall, + ParallelFunctionCall, + AsyncParallelFunctionCall, +) + + +def get_function_schemas( + functions: Iterable[Callable[..., R]] | None, + output_types: Iterable[type[T]], +) -> Iterable[FunctionSchema[FunctionCall[R] | T]]: + return [ + *(FunctionCallFunctionSchema(f) for f in functions or []), # type: ignore[list-item] + *( + function_schema_for_type(type_) + for type_ in output_types + if not is_origin_subclass(type_, _NON_FUNCTION_CALL_TYPES) # type: ignore[list-item] + ), + ] + + +def get_async_function_schemas( + functions: Iterable[Callable[..., R]] | None, + output_types: Iterable[type[T]], +) -> Iterable[FunctionSchema[FunctionCall[R] | T]]: + return [ + *(FunctionCallFunctionSchema(f) for f in functions or []), # type: ignore[list-item] + *( + async_function_schema_for_type(type_) + for type_ in output_types + if not is_origin_subclass(type_, _NON_FUNCTION_CALL_TYPES) # type: ignore[list-item] + ), + ] diff --git a/src/magentic/chat_model/litellm_chat_model.py b/src/magentic/chat_model/litellm_chat_model.py index 68ea72f5..1bbde01e 100644 --- a/src/magentic/chat_model/litellm_chat_model.py +++ b/src/magentic/chat_model/litellm_chat_model.py @@ -1,53 +1,108 @@ from collections.abc import Callable, Iterable, Sequence -from itertools import chain -from typing import Any, TypeVar, cast, overload +from typing import Any, Literal, TypeVar, cast, overload -from openai.types.chat import ChatCompletionToolChoiceOptionParam +import openai +from openai.lib.streaming.chat._completions import ChatCompletionStreamState +from openai.types.chat import ChatCompletionNamedToolChoiceParam -from magentic.chat_model.base import ( - ChatModel, - avalidate_str_content, - validate_str_content, -) +from magentic.chat_model.base import ChatModel, aparse_stream, parse_stream from magentic.chat_model.function_schema import ( - FunctionCallFunctionSchema, - async_function_schema_for_type, - function_schema_for_type, -) -from magentic.chat_model.message import ( - AssistantMessage, - Message, + get_async_function_schemas, + get_function_schemas, ) +from magentic.chat_model.message import AssistantMessage, Message, Usage, _RawMessage from magentic.chat_model.openai_chat_model import ( - STR_OR_FUNCTIONCALL_TYPE, - AsyncFunctionToolSchema, BaseFunctionToolSchema, - FunctionToolSchema, - _aparse_streamed_tool_calls, - _parse_streamed_tool_calls, - discard_none_arguments, message_to_openai_message, ) -from magentic.function_call import ( - AsyncParallelFunctionCall, - ParallelFunctionCall, +from magentic.chat_model.stream import ( + AsyncOutputStream, + FunctionCallChunk, + OutputStream, + StreamParser, + StreamState, ) from magentic.streaming import ( AsyncStreamedStr, StreamedStr, - achain, - async_iter, ) -from magentic.typing import is_any_origin_subclass, is_origin_subclass +from magentic.typing import is_any_origin_subclass try: import litellm + from litellm.litellm_core_utils.streaming_handler import ( # type: ignore[attr-defined] + StreamingChoices, + ) from litellm.types.utils import ModelResponse except ImportError as error: msg = "To use LitellmChatModel you must install the `litellm` package using `pip install 'magentic[litellm]'`." raise ImportError(msg) from error +class LitellmStreamParser(StreamParser[ModelResponse]): + def is_content(self, item: ModelResponse) -> bool: + assert isinstance(item.choices[0], StreamingChoices) # noqa: S101 + return bool(item.choices[0].delta.content) + + def get_content(self, item: ModelResponse) -> str | None: + assert isinstance(item.choices[0], StreamingChoices) # noqa: S101 + assert isinstance(item.choices[0].delta.content, str | None) # noqa: S101 + return item.choices[0].delta.content + + def is_tool_call(self, item: ModelResponse) -> bool: + assert isinstance(item.choices[0], StreamingChoices) # noqa: S101 + return bool(item.choices[0].delta.tool_calls) + + def iter_tool_calls(self, item: ModelResponse) -> Iterable[FunctionCallChunk]: + assert isinstance(item.choices[0], StreamingChoices) # noqa: S101 + if item.choices and item.choices[0].delta.tool_calls: + for tool_call in item.choices[0].delta.tool_calls: + if tool_call.function: + yield FunctionCallChunk( + id=tool_call.id, + name=tool_call.function.name, + args=tool_call.function.arguments, + ) + + +class LitellmStreamState(StreamState[ModelResponse]): + def __init__(self) -> None: + self._chat_completion_stream_state = ChatCompletionStreamState( + input_tools=openai.NOT_GIVEN, + response_format=openai.NOT_GIVEN, + ) + self.usage_ref: list[Usage] = [] + + def update(self, item: ModelResponse) -> None: + # Patch attributes required inside ChatCompletionStreamState.handle_chunk + if not hasattr(item, "usage"): + # litellm requires usage is not None for its total usage calculation + item.usage = litellm.Usage() # type: ignore[attr-defined] + if not hasattr(item, "refusal"): + assert isinstance(item.choices[0], StreamingChoices) # noqa: S101 + item.choices[0].delta.refusal = None # type: ignore[attr-defined] + self._chat_completion_stream_state.handle_chunk(item) # type: ignore[arg-type] + usage = cast(litellm.Usage, item.usage) # type: ignore[attr-defined,name-defined] + # Ignore usages with 0 tokens + if usage and usage.prompt_tokens and usage.completion_tokens: + assert not self.usage_ref # noqa: S101 + self.usage_ref.append( + Usage( + input_tokens=usage.prompt_tokens, + output_tokens=usage.completion_tokens, + ) + ) + + @property + def current_message_snapshot(self) -> Message[Any]: + snapshot = self._chat_completion_stream_state.current_completion_snapshot + message = snapshot.choices[0].message + # Fix incorrectly concatenated role + message.role = "assistant" + # TODO: Possible to return AssistantMessage here? + return _RawMessage(message.model_dump()) + + R = TypeVar("R") @@ -100,7 +155,7 @@ def _get_tool_choice( *, tool_schemas: Sequence[BaseFunctionToolSchema[Any]], allow_string_output: bool, - ) -> ChatCompletionToolChoiceOptionParam | None: + ) -> ChatCompletionNamedToolChoiceParam | Literal["required"] | None: """Create the tool choice argument.""" if allow_string_output: return None @@ -138,20 +193,14 @@ def complete( ) -> AssistantMessage[str] | AssistantMessage[R]: """Request an LLM message.""" if output_types is None: - output_types = [] if functions else cast(list[type[R]], [str]) + output_types = cast(Iterable[type[R]], [] if functions else [str]) - function_schemas = [FunctionCallFunctionSchema(f) for f in functions or []] + [ - function_schema_for_type(type_) - for type_ in output_types - if not is_origin_subclass(type_, STR_OR_FUNCTIONCALL_TYPE) - ] - tool_schemas = [FunctionToolSchema(schema) for schema in function_schemas] + function_schemas = get_function_schemas(functions, output_types) + tool_schemas = [BaseFunctionToolSchema(schema) for schema in function_schemas] - str_in_output_types = is_any_origin_subclass(output_types, str) - streamed_str_in_output_types = is_any_origin_subclass(output_types, StreamedStr) - allow_string_output = str_in_output_types or streamed_str_in_output_types + allow_string_output = is_any_origin_subclass(output_types, (str, StreamedStr)) - response = discard_none_arguments(litellm.completion)( + response = litellm.completion( model=self.model, messages=[message_to_openai_message(m) for m in messages], api_base=self.api_base, @@ -160,51 +209,21 @@ def complete( metadata=self.metadata, stop=stop, stream=True, + # TODO: Add usage for LitellmChatModel temperature=self.temperature, tools=[schema.to_dict() for schema in tool_schemas] or None, tool_choice=self._get_tool_choice( tool_schemas=tool_schemas, allow_string_output=allow_string_output - ), + ), # type: ignore[arg-type,unused-ignore] ) assert not isinstance(response, ModelResponse) # noqa: S101 - - first_chunk = next(response) - # Azure OpenAI sends a chunk with empty choices first - if len(first_chunk.choices) == 0: - first_chunk = next(response) - if ( - first_chunk.choices[0].delta.content is None - and first_chunk.choices[0].delta.tool_calls is None - ): - first_chunk = next(response) - response = chain([first_chunk], response) - - # Check tool calls before content because both might be present - if first_chunk.choices[0].delta.tool_calls is not None: - tool_calls = _parse_streamed_tool_calls(response, tool_schemas) - if is_any_origin_subclass(output_types, ParallelFunctionCall): - content = ParallelFunctionCall(tool_calls) - return AssistantMessage(content) # type: ignore[return-value] - # Take only the first tool_call, silently ignore extra chunks - # TODO: Create generator here that raises error or warns if multiple tool_calls - content = next(tool_calls) - return AssistantMessage(content) # type: ignore[return-value] - - if first_chunk.choices[0].delta.content is not None: - streamed_str = StreamedStr( - chunk.choices[0].delta.get("content", None) - for chunk in response - if chunk.choices[0].delta.get("content", None) is not None - ) - str_content = validate_str_content( - streamed_str, - allow_string_output=allow_string_output, - streamed=streamed_str_in_output_types, - ) - return AssistantMessage(str_content) # type: ignore[return-value] - - msg = f"Could not determine response type for first chunk: {first_chunk.model_dump_json()}" - raise ValueError(msg) + stream = OutputStream( + stream=response, + function_schemas=function_schemas, + parser=LitellmStreamParser(), + state=LitellmStreamState(), + ) + return AssistantMessage(parse_stream(stream, output_types)) @overload async def acomplete( @@ -236,22 +255,16 @@ async def acomplete( ) -> AssistantMessage[str] | AssistantMessage[R]: """Async version of `complete`.""" if output_types is None: - output_types = [] if functions else cast(list[type[R]], [str]) - - function_schemas = [FunctionCallFunctionSchema(f) for f in functions or []] + [ - async_function_schema_for_type(type_) - for type_ in output_types - if not is_origin_subclass(type_, STR_OR_FUNCTIONCALL_TYPE) - ] - tool_schemas = [AsyncFunctionToolSchema(schema) for schema in function_schemas] - - str_in_output_types = is_any_origin_subclass(output_types, str) - async_streamed_str_in_output_types = is_any_origin_subclass( - output_types, AsyncStreamedStr + output_types = cast(Iterable[type[R]], [] if functions else [str]) + + function_schemas = get_async_function_schemas(functions, output_types) + tool_schemas = [BaseFunctionToolSchema(schema) for schema in function_schemas] + + allow_string_output = is_any_origin_subclass( + output_types, (str, AsyncStreamedStr) ) - allow_string_output = str_in_output_types or async_streamed_str_in_output_types - response = await discard_none_arguments(litellm.acompletion)( + response = await litellm.acompletion( model=self.model, messages=[message_to_openai_message(m) for m in messages], api_base=self.api_base, @@ -260,47 +273,18 @@ async def acomplete( metadata=self.metadata, stop=stop, stream=True, + # TODO: Add usage for LitellmChatModel temperature=self.temperature, tools=[schema.to_dict() for schema in tool_schemas] or None, tool_choice=self._get_tool_choice( tool_schemas=tool_schemas, allow_string_output=allow_string_output - ), + ), # type: ignore[arg-type,unused-ignore] ) assert not isinstance(response, ModelResponse) # noqa: S101 - - first_chunk = await anext(response) - # Azure OpenAI sends a chunk with empty choices first - if len(first_chunk.choices) == 0: - first_chunk = await anext(response) - if ( - first_chunk.choices[0].delta.content is None - and first_chunk.choices[0].delta.tool_calls is None - ): - first_chunk = await anext(response) - response = achain(async_iter([first_chunk]), response) - - # Check tool calls before content because both might be present - if first_chunk.choices[0].delta.tool_calls is not None: - tool_calls = _aparse_streamed_tool_calls(response, tool_schemas) - if is_any_origin_subclass(output_types, AsyncParallelFunctionCall): - content = AsyncParallelFunctionCall(tool_calls) - return AssistantMessage(content) # type: ignore[return-value] - # Take only the first tool_call, silently ignore extra chunks - content = await anext(tool_calls) - return AssistantMessage(content) # type: ignore[return-value] - - if first_chunk.choices[0].delta.content is not None: - async_streamed_str = AsyncStreamedStr( - chunk.choices[0].delta.get("content", None) - async for chunk in response - if chunk.choices[0].delta.get("content", None) is not None - ) - str_content = await avalidate_str_content( - async_streamed_str, - allow_string_output=allow_string_output, - streamed=async_streamed_str_in_output_types, - ) - return AssistantMessage(str_content) # type: ignore[return-value] - - msg = f"Could not determine response type for first chunk: {first_chunk.model_dump_json()}" - raise ValueError(msg) + stream = AsyncOutputStream( + stream=response, + function_schemas=function_schemas, + parser=LitellmStreamParser(), + state=LitellmStreamState(), + ) + return AssistantMessage(await aparse_stream(stream, output_types)) diff --git a/src/magentic/chat_model/openai_chat_model.py b/src/magentic/chat_model/openai_chat_model.py index c2eaf146..9461c6fb 100644 --- a/src/magentic/chat_model/openai_chat_model.py +++ b/src/magentic/chat_model/openai_chat_model.py @@ -1,6 +1,5 @@ import base64 from collections.abc import ( - AsyncIterable, AsyncIterator, Callable, Iterable, @@ -8,36 +7,33 @@ Sequence, ) from enum import Enum -from functools import singledispatch, wraps -from itertools import chain, groupby -from typing import Any, Generic, Literal, ParamSpec, TypeVar, cast, overload +from functools import singledispatch +from typing import Any, Generic, Literal, TypeVar, cast, overload import filetype import openai +from openai.lib.streaming.chat._completions import ChatCompletionStreamState from openai.types.chat import ( ChatCompletionChunk, ChatCompletionMessageParam, - ChatCompletionMessageToolCallParam, + ChatCompletionNamedToolChoiceParam, ChatCompletionStreamOptionsParam, ChatCompletionToolChoiceOptionParam, ChatCompletionToolParam, ) -from openai.types.chat.chat_completion_chunk import ChoiceDeltaToolCall -from pydantic import ValidationError from magentic.chat_model.base import ( ChatModel, - ToolSchemaParseError, - avalidate_str_content, - validate_str_content, + aparse_stream, + parse_stream, ) from magentic.chat_model.function_schema import ( - AsyncFunctionSchema, BaseFunctionSchema, FunctionCallFunctionSchema, FunctionSchema, - async_function_schema_for_type, function_schema_for_type, + get_async_function_schemas, + get_function_schemas, ) from magentic.chat_model.message import ( AssistantMessage, @@ -48,6 +44,13 @@ UserMessage, _RawMessage, ) +from magentic.chat_model.stream import ( + AsyncOutputStream, + FunctionCallChunk, + OutputStream, + StreamParser, + StreamState, +) from magentic.function_call import ( AsyncParallelFunctionCall, FunctionCall, @@ -57,15 +60,8 @@ from magentic.streaming import ( AsyncStreamedStr, StreamedStr, - aapply, - achain, - agroupby, - apeek, - apply, - async_iter, - peek, ) -from magentic.typing import is_any_origin_subclass, is_origin_subclass +from magentic.typing import is_any_origin_subclass from magentic.vision import UserImageMessage @@ -85,8 +81,10 @@ def message_to_openai_message(message: Message[Any]) -> ChatCompletionMessagePar @message_to_openai_message.register(_RawMessage) def _(message: _RawMessage[Any]) -> ChatCompletionMessageParam: - # TODO: Validate the message content - return message.content # type: ignore[no-any-return] + assert isinstance(message.content, dict) # noqa: S101 + assert "role" in message.content # noqa: S101 + assert "content" in message.content # noqa: S101 + return cast(ChatCompletionMessageParam, message.content) @message_to_openai_message.register @@ -232,249 +230,90 @@ class BaseFunctionToolSchema(Generic[BaseFunctionSchemaT]): def __init__(self, function_schema: BaseFunctionSchemaT): self._function_schema = function_schema - def as_tool_choice(self) -> ChatCompletionToolChoiceOptionParam: + def as_tool_choice(self) -> ChatCompletionNamedToolChoiceParam: return {"type": "function", "function": {"name": self._function_schema.name}} def to_dict(self) -> ChatCompletionToolParam: return {"type": "function", "function": self._function_schema.dict()} - def matches(self, tool_call: ChoiceDeltaToolCall) -> bool: - return bool( - # TODO: Add back tool_call.type == "function" when LiteLLM Mistral fixed - # https://github.com/BerriAI/litellm/issues/2645 - tool_call.function and self._function_schema.name == tool_call.function.name - ) - -# TODO: Generalize this to BaseToolSchema when that is created -BeseToolSchemaT = TypeVar("BeseToolSchemaT", bound=BaseFunctionToolSchema[Any]) +class OpenaiStreamParser(StreamParser[ChatCompletionChunk]): + def is_content(self, item: ChatCompletionChunk) -> bool: + return bool(item.choices and item.choices[0].delta.content) + def get_content(self, item: ChatCompletionChunk) -> str | None: + if item.choices and item.choices[0].delta.content: + return item.choices[0].delta.content + return None -def select_tool_schema( - tool_call: ChoiceDeltaToolCall, tools_schemas: list[BeseToolSchemaT] -) -> BeseToolSchemaT: - """Select the tool schema based on the response chunk.""" - for tool_schema in tools_schemas: - if tool_schema.matches(tool_call): - return tool_schema + def is_tool_call(self, item: ChatCompletionChunk) -> bool: + return bool(item.choices and item.choices[0].delta.tool_calls) - msg = f"Unknown tool call: {tool_call.model_dump_json()}" - raise ValueError(msg) # TODO: Create `UnknownToolCallError` for this + def iter_tool_calls(self, item: ChatCompletionChunk) -> Iterator[FunctionCallChunk]: + if item.choices and item.choices[0].delta.tool_calls: + for tool_call in item.choices[0].delta.tool_calls: + if tool_call.function: + yield FunctionCallChunk( + id=tool_call.id, + name=tool_call.function.name, + args=tool_call.function.arguments, + ) -class FunctionToolSchema(BaseFunctionToolSchema[FunctionSchema[T]]): - def parse_tool_call(self, chunks: Iterable[ChoiceDeltaToolCall]) -> T: - return self._function_schema.parse_args( - chunk.function.arguments - for chunk in chunks - if chunk.function and chunk.function.arguments is not None - ) +class OpenaiStreamState(StreamState[ChatCompletionChunk]): + """Tracks the state of the OpenAI model output stream. + - message snapshot + - usage + - stop reason + """ -class AsyncFunctionToolSchema(BaseFunctionToolSchema[AsyncFunctionSchema[T]]): - async def aparse_tool_call(self, chunks: AsyncIterable[ChoiceDeltaToolCall]) -> T: - return await self._function_schema.aparse_args( - chunk.function.arguments - async for chunk in chunks - if chunk.function and chunk.function.arguments is not None + def __init__(self) -> None: + self._chat_completion_stream_state = ChatCompletionStreamState( + input_tools=openai.NOT_GIVEN, + response_format=openai.NOT_GIVEN, ) - - -def _get_tool_call_id_for_chunk(tool_call: ChoiceDeltaToolCall) -> Any: - """Returns an id that is consistent for chunks from the same tool_call.""" - # openai keeps index consistent for chunks from the same tool_call, but id is null - # mistral has null index, but keeps id consistent - return tool_call.index if tool_call.index is not None else tool_call.id - - -def _iter_streamed_tool_calls( - response: Iterable[ChatCompletionChunk], -) -> Iterator[Iterator[ChoiceDeltaToolCall]]: - """Group tool_call chunks into separate iterators.""" - all_tool_call_chunks = ( - tool_call - for chunk in response - if chunk.choices and chunk.choices[0].delta.tool_calls - for tool_call in chunk.choices[0].delta.tool_calls - ) - for _, tool_call_chunks in groupby( - all_tool_call_chunks, _get_tool_call_id_for_chunk - ): - yield tool_call_chunks - - -async def _aiter_streamed_tool_calls( - response: AsyncIterable[ChatCompletionChunk], -) -> AsyncIterator[AsyncIterator[ChoiceDeltaToolCall]]: - """Async version of `_iter_streamed_tool_calls`.""" - all_tool_call_chunks = ( - tool_call - async for chunk in response - if chunk.choices and chunk.choices[0].delta.tool_calls - for tool_call in chunk.choices[0].delta.tool_calls - ) - async for _, tool_call_chunks in agroupby( - all_tool_call_chunks, _get_tool_call_id_for_chunk - ): - yield tool_call_chunks - - -def _parse_streamed_tool_calls( - response: Iterable[ChatCompletionChunk], - tool_schemas: list[FunctionToolSchema[T]], -) -> Iterator[T]: - cached_response: list[ChatCompletionChunk] = [] - response = apply(cached_response.append, response) - try: - for tool_call_chunks in _iter_streamed_tool_calls(response): - first_chunk, tool_call_chunks = peek(tool_call_chunks) - tool_schema = select_tool_schema(first_chunk, tool_schemas) - tool_call = tool_schema.parse_tool_call(tool_call_chunks) - yield tool_call - # TODO: Catch/raise unknown tool call error here - except ValidationError as e: - raw_message = _join_streamed_tool_calls_to_message(cached_response) - raise ToolSchemaParseError( - output_message=raw_message, - tool_call_id=raw_message.content["tool_calls"][0]["id"], # type: ignore[index,unused-ignore] - validation_error=e, - ) from e - - -async def _aparse_streamed_tool_calls( - response: AsyncIterable[ChatCompletionChunk], - tool_schemas: list[AsyncFunctionToolSchema[T]], -) -> AsyncIterator[T]: - cached_response: list[ChatCompletionChunk] = [] - response = aapply(cached_response.append, response) - try: - async for tool_call_chunks in _aiter_streamed_tool_calls(response): - first_chunk, tool_call_chunks = await apeek(tool_call_chunks) - tool_schema = select_tool_schema(first_chunk, tool_schemas) - tool_call = await tool_schema.aparse_tool_call(tool_call_chunks) - yield tool_call - # TODO: Catch/raise unknown tool call error here - except ValidationError as e: - raw_message = _join_streamed_tool_calls_to_message(cached_response) - raise ToolSchemaParseError( - output_message=raw_message, - tool_call_id=raw_message.content["tool_calls"][0]["id"], # type: ignore[index,unused-ignore] - validation_error=e, - ) from e - - -def _join_streamed_tool_call( - tool_call_deltas: Iterable[ChoiceDeltaToolCall], -) -> ChatCompletionMessageToolCallParam: - """Join chunks from a single streamed tool call into an OpenAI tool call dict.""" - tool_id: str | None = None - tool_type: Literal["function"] = "function" - function_name: list[str] = [] - function_arguments: list[str] = [] - for tool_call_delta in tool_call_deltas: - if tool_call_delta.id: - tool_id = tool_call_delta.id - if tool_call_delta.type: - tool_type = tool_call_delta.type - if tool_call_delta.function: - if tool_call_delta.function.name: - function_name.append(tool_call_delta.function.name) - if tool_call_delta.function.arguments: - function_arguments.append(tool_call_delta.function.arguments) - return { - "id": tool_id or _create_unique_id(), - "type": tool_type, - "function": { - "name": "".join(function_name), - "arguments": "".join(function_arguments), - }, - } - - -def _join_streamed_tool_calls_to_message( - response: Iterable[ChatCompletionChunk], - # TODO: Type as ChatCompletionAssistantMessageParam. Issue: https://github.com/pydantic/pydantic/issues/10105 -) -> _RawMessage[Any]: - """Join streamed tool calls into an OpenAI chat completion message.""" - return _RawMessage( - { - "role": OpenaiMessageRole.ASSISTANT.value, - "content": None, - "tool_calls": [ - _join_streamed_tool_call(tool_call_chunks) - for tool_call_chunks in _iter_streamed_tool_calls(response) - ], - } - ) - - -def _create_usage_ref( - response: Iterable[ChatCompletionChunk], -) -> tuple[list[Usage], Iterator[ChatCompletionChunk]]: - """Returns a pointer to a Usage object that is created at the end of the response.""" - usage_ref: list[Usage] = [] - - def generator( - response: Iterable[ChatCompletionChunk], - ) -> Iterator[ChatCompletionChunk]: - for chunk in response: - if chunk.usage: - usage = Usage( - input_tokens=chunk.usage.prompt_tokens, - output_tokens=chunk.usage.completion_tokens, - ) - usage_ref.append(usage) - yield chunk - - return usage_ref, generator(response) - - -def _create_usage_ref_async( - response: AsyncIterable[ChatCompletionChunk], -) -> tuple[list[Usage], AsyncIterator[ChatCompletionChunk]]: - """Async version of `_create_usage_ref`.""" - usage_ref: list[Usage] = [] - - async def agenerator( - response: AsyncIterable[ChatCompletionChunk], - ) -> AsyncIterator[ChatCompletionChunk]: - async for chunk in response: - if chunk.usage: - usage = Usage( - input_tokens=chunk.usage.prompt_tokens, - output_tokens=chunk.usage.completion_tokens, + self.usage_ref: list[Usage] = [] + + # Keep track of tool call index to add this to Mistral tool calls + self._current_tool_call_index: int = -1 + self._seen_tool_call_ids: set[str] = set() + + def update(self, item: ChatCompletionChunk) -> None: + # Add tool call index for Mistral tool calls to make compatible with OpenAI + # TODO: Remove this fix when MistralChatModel switched to mistral python package + if item.choices: + for tool_call_chunk in item.choices[0].delta.tool_calls or []: + if ( + tool_call_chunk.id is not None + and tool_call_chunk.id not in self._seen_tool_call_ids + ): + self._current_tool_call_index += 1 + self._seen_tool_call_ids.add(tool_call_chunk.id) + tool_call_chunk.index = self._current_tool_call_index + self._chat_completion_stream_state.handle_chunk(item) + if item.usage: + assert not self.usage_ref # noqa: S101 + self.usage_ref.append( + Usage( + input_tokens=item.usage.prompt_tokens, + output_tokens=item.usage.completion_tokens, ) - usage_ref.append(usage) - yield chunk - - return usage_ref, agenerator(response) - - -P = ParamSpec("P") -R = TypeVar("R") - + ) -def discard_none_arguments(func: Callable[P, R]) -> Callable[P, R]: - """Decorator to discard function arguments with value `None`""" + @property + def current_message_snapshot(self) -> Message[Any]: + snapshot = self._chat_completion_stream_state.current_completion_snapshot + message = snapshot.choices[0].message + # TODO: Possible to return AssistantMessage here? + return _RawMessage(message.model_dump()) - @wraps(func) - def wrapped(*args: P.args, **kwargs: P.kwargs) -> R: - non_none_kwargs = { - key: value for key, value in kwargs.items() if value is not None - } - return func(*args, **non_none_kwargs) # type: ignore[arg-type] - return wrapped +def _if_given(value: T | None) -> T | openai.NotGiven: + return value if value is not None else openai.NOT_GIVEN -STR_OR_FUNCTIONCALL_TYPE = ( - str, - StreamedStr, - AsyncStreamedStr, - FunctionCall, - ParallelFunctionCall, - AsyncParallelFunctionCall, -) +R = TypeVar("R") class OpenaiChatModel(ChatModel): @@ -601,36 +440,28 @@ def complete( output_types: Iterable[type[R]] | None = None, *, stop: list[str] | None = None, + # TODO: Add type hint for function call ? ) -> AssistantMessage[str] | AssistantMessage[R]: """Request an LLM message.""" if output_types is None: - output_types = [] if functions else cast(list[type[R]], [str]) + output_types = cast(Iterable[type[R]], [] if functions else [str]) - # TODO: Check that Function calls types match functions - function_schemas = [FunctionCallFunctionSchema(f) for f in functions or []] + [ - function_schema_for_type(type_) - for type_ in output_types - if not is_origin_subclass(type_, STR_OR_FUNCTIONCALL_TYPE) - ] - tool_schemas = [FunctionToolSchema(schema) for schema in function_schemas] - - str_in_output_types = is_any_origin_subclass(output_types, str) - streamed_str_in_output_types = is_any_origin_subclass(output_types, StreamedStr) - allow_string_output = str_in_output_types or streamed_str_in_output_types - - response: Iterator[ChatCompletionChunk] = discard_none_arguments( - self._client.chat.completions.create - )( + function_schemas = get_function_schemas(functions, output_types) + tool_schemas = [BaseFunctionToolSchema(schema) for schema in function_schemas] + + allow_string_output = is_any_origin_subclass(output_types, (str, StreamedStr)) + + response: Iterator[ChatCompletionChunk] = self._client.chat.completions.create( model=self.model, messages=_add_missing_tool_calls_responses( [message_to_openai_message(m) for m in messages] ), - max_tokens=self.max_tokens, - seed=self.seed, - stop=stop, + max_tokens=_if_given(self.max_tokens), + seed=_if_given(self.seed), + stop=_if_given(stop), stream=True, stream_options=self._get_stream_options(), - temperature=self.temperature, + temperature=_if_given(self.temperature), tools=[schema.to_dict() for schema in tool_schemas] or openai.NOT_GIVEN, tool_choice=self._get_tool_choice( tool_schemas=tool_schemas, allow_string_output=allow_string_output @@ -639,44 +470,15 @@ def complete( tools_specified=bool(tool_schemas), output_types=output_types ), ) - usage_ref, response = _create_usage_ref(response) - - first_chunk = next(response) - # Azure OpenAI sends a chunk with empty choices first - if len(first_chunk.choices) == 0: - first_chunk = next(response) - if ( - # Mistral tool call first chunk has content "" - not first_chunk.choices[0].delta.content - and not first_chunk.choices[0].delta.tool_calls - ): - first_chunk = next(response) - response = chain([first_chunk], response) - - if first_chunk.choices[0].delta.content: - streamed_str = StreamedStr( - chunk.choices[0].delta.content - for chunk in response - if chunk.choices and chunk.choices[0].delta.content is not None - ) - str_content = validate_str_content( - streamed_str, - allow_string_output=allow_string_output, - streamed=streamed_str_in_output_types, - ) - return AssistantMessage._with_usage(str_content, usage_ref) # type: ignore[return-value] - - if first_chunk.choices[0].delta.tool_calls: - tool_calls = _parse_streamed_tool_calls(response, tool_schemas) - if is_any_origin_subclass(output_types, ParallelFunctionCall): - content = ParallelFunctionCall(tool_calls) - return AssistantMessage._with_usage(content, usage_ref) # type: ignore[return-value] - # Take only the first tool_call, silently ignore extra chunks - content = next(tool_calls) - return AssistantMessage._with_usage(content, usage_ref) # type: ignore[return-value] - - msg = f"Could not determine response type for first chunk: {first_chunk.model_dump_json()}" - raise ValueError(msg) + stream = OutputStream( + response, + function_schemas=function_schemas, + parser=OpenaiStreamParser(), + state=OpenaiStreamState(), + ) + return AssistantMessage._with_usage( + parse_stream(stream, output_types), usage_ref=stream.usage_ref + ) @overload async def acomplete( @@ -710,32 +512,26 @@ async def acomplete( if output_types is None: output_types = [] if functions else cast(list[type[R]], [str]) - function_schemas = [FunctionCallFunctionSchema(f) for f in functions or []] + [ - async_function_schema_for_type(type_) - for type_ in output_types - if not is_origin_subclass(type_, STR_OR_FUNCTIONCALL_TYPE) - ] - tool_schemas = [AsyncFunctionToolSchema(schema) for schema in function_schemas] + function_schemas = get_async_function_schemas(functions, output_types) + tool_schemas = [BaseFunctionToolSchema(schema) for schema in function_schemas] - str_in_output_types = is_any_origin_subclass(output_types, str) - async_streamed_str_in_output_types = is_any_origin_subclass( - output_types, AsyncStreamedStr + allow_string_output = is_any_origin_subclass( + output_types, (str, AsyncStreamedStr) ) - allow_string_output = str_in_output_types or async_streamed_str_in_output_types - response: AsyncIterator[ChatCompletionChunk] = await discard_none_arguments( - self._async_client.chat.completions.create - )( + response: AsyncIterator[ + ChatCompletionChunk + ] = await self._async_client.chat.completions.create( model=self.model, messages=_add_missing_tool_calls_responses( [message_to_openai_message(m) for m in messages] ), - max_tokens=self.max_tokens, - seed=self.seed, - stop=stop, + max_tokens=_if_given(self.max_tokens), + seed=_if_given(self.seed), + stop=_if_given(stop), stream=True, stream_options=self._get_stream_options(), - temperature=self.temperature, + temperature=_if_given(self.temperature), tools=[schema.to_dict() for schema in tool_schemas] or openai.NOT_GIVEN, tool_choice=self._get_tool_choice( tool_schemas=tool_schemas, allow_string_output=allow_string_output @@ -744,41 +540,12 @@ async def acomplete( tools_specified=bool(tool_schemas), output_types=output_types ), ) - usage_ref, response = _create_usage_ref_async(response) - - first_chunk = await anext(response) - # Azure OpenAI sends a chunk with empty choices first - if len(first_chunk.choices) == 0: - first_chunk = await anext(response) - if ( - # Mistral tool call first chunk has content "" - not first_chunk.choices[0].delta.content - and not first_chunk.choices[0].delta.tool_calls - ): - first_chunk = await anext(response) - response = achain(async_iter([first_chunk]), response) - - if first_chunk.choices[0].delta.content: - async_streamed_str = AsyncStreamedStr( - chunk.choices[0].delta.content - async for chunk in response - if chunk.choices and chunk.choices[0].delta.content is not None - ) - str_content = await avalidate_str_content( - async_streamed_str, - allow_string_output=allow_string_output, - streamed=async_streamed_str_in_output_types, - ) - return AssistantMessage._with_usage(str_content, usage_ref) # type: ignore[return-value] - - if first_chunk.choices[0].delta.tool_calls: - tool_calls = _aparse_streamed_tool_calls(response, tool_schemas) - if is_any_origin_subclass(output_types, AsyncParallelFunctionCall): - content = AsyncParallelFunctionCall(tool_calls) - return AssistantMessage._with_usage(content, usage_ref) # type: ignore[return-value] - # Take only the first tool_call, silently ignore extra chunks - content = await anext(tool_calls) - return AssistantMessage._with_usage(content, usage_ref) # type: ignore[return-value] - - msg = f"Could not determine response type for first chunk: {first_chunk.model_dump_json()}" - raise ValueError(msg) + stream = AsyncOutputStream( + response, + function_schemas=function_schemas, + parser=OpenaiStreamParser(), + state=OpenaiStreamState(), + ) + return AssistantMessage._with_usage( + await aparse_stream(stream, output_types), usage_ref=stream.usage_ref + ) diff --git a/src/magentic/chat_model/retry_chat_model.py b/src/magentic/chat_model/retry_chat_model.py index 8d008c3d..a5c901b6 100644 --- a/src/magentic/chat_model/retry_chat_model.py +++ b/src/magentic/chat_model/retry_chat_model.py @@ -29,10 +29,14 @@ def __init__( self._max_retries = max_retries # TODO: Make this public to allow modifying error handling behavior + # User should be able to add handlers to instance using decorator + # e.g. `@my_retry_chat_model.exception_handler(exc_type)` + # TODO: Add exception base class for those with output_message attribute @singledispatchmethod def _make_retry_messages(self, error: Exception) -> list[Message[Any]]: raise NotImplementedError + # TODO: Catch UnknownToolError here @_make_retry_messages.register def _(self, error: ToolSchemaParseError) -> list[Message[Any]]: return [ diff --git a/src/magentic/chat_model/stream.py b/src/magentic/chat_model/stream.py new file mode 100644 index 00000000..1ca3c79b --- /dev/null +++ b/src/magentic/chat_model/stream.py @@ -0,0 +1,275 @@ +from abc import ABC, abstractmethod +from collections.abc import AsyncIterator, Iterable, Iterator +from itertools import chain +from typing import Any, Generic, NamedTuple, TypeVar + +from pydantic import ValidationError + +from magentic.chat_model.base import ToolSchemaParseError, UnknownToolError +from magentic.chat_model.function_schema import ( + AsyncFunctionSchema, + FunctionSchema, + select_function_schema, +) +from magentic.chat_model.message import Message, Usage +from magentic.streaming import ( + AsyncStreamedStr, + StreamedStr, + aapply, + achain, + apply, + async_iter, +) + +ItemT = TypeVar("ItemT") +OutputT = TypeVar("OutputT") + + +class FunctionCallChunk(NamedTuple): + id: str | None + name: str | None + args: str | None + + +class StreamParser(ABC, Generic[ItemT]): + @abstractmethod + def is_content(self, item: ItemT) -> bool: ... + + @abstractmethod + def get_content(self, item: ItemT) -> str | None: ... + + @abstractmethod + def is_tool_call(self, item: ItemT) -> bool: ... + + @abstractmethod + def iter_tool_calls(self, item: ItemT) -> Iterable[FunctionCallChunk]: ... + + +class StreamState(ABC, Generic[ItemT]): + """Tracks the state of the LLM output stream. + + - message snapshot + - usage + - stop reason + """ + + usage_ref: list[Usage] + + @abstractmethod + def update(self, item: ItemT) -> None: ... + + @property + @abstractmethod + def current_message_snapshot(self) -> Message[Any]: ... + + +class OutputStream(Generic[ItemT, OutputT]): + """Converts streamed LLM output into a stream of magentic objects.""" + + def __init__( + self, + stream: Iterator[ItemT], + function_schemas: Iterable[FunctionSchema[OutputT]], + parser: StreamParser[ItemT], + state: StreamState[ItemT], + ): + self._stream = stream + self._function_schemas = function_schemas + self._parser = parser + self._state = state + + self._iterator = self.__stream__() + + def __next__(self) -> StreamedStr | OutputT: + return self._iterator.__next__() + + def __iter__(self) -> Iterator[StreamedStr | OutputT]: + yield from self._iterator + + def _streamed_str( + self, stream: Iterator[ItemT], current_item_ref: list[ItemT] + ) -> Iterator[str]: + for item in stream: + if content := self._parser.get_content(item): + yield content + if self._parser.is_tool_call(item): + # TODO: Check if output types allow for early return and raise if not + assert not current_item_ref # noqa: S101 + current_item_ref.append(item) + return + + def _tool_call( + self, + stream: Iterator[FunctionCallChunk], + current_tool_call_ref: list[FunctionCallChunk], + current_tool_call_id: str, + ) -> Iterator[str]: + for item in stream: + # Only end the stream if we encounter a new tool call + # so that the whole stream is consumed including stop_reason/usage chunks + if item.id and item.id != current_tool_call_id: + # TODO: Check if output types allow for early return and raise if not + assert not current_tool_call_ref # noqa: S101 + current_tool_call_ref.append(item) + return + if item.args: + yield item.args + + def __stream__(self) -> Iterator[StreamedStr | OutputT]: + stream = apply(self._state.update, self._stream) + current_item_ref = [next(stream)] + while current_item_ref: + current_item = current_item_ref.pop() + if self._parser.is_content(current_item): + stream = chain([current_item], stream) + yield StreamedStr(self._streamed_str(stream, current_item_ref)) + elif self._parser.is_tool_call(current_item): + tool_calls_stream = ( + tool_call_chunk + for item in chain([current_item], stream) + for tool_call_chunk in self._parser.iter_tool_calls(item) + ) + tool_call_ref = [next(tool_calls_stream)] + while tool_call_ref: + current_tool_call_chunk = tool_call_ref.pop() + current_tool_call_id = current_tool_call_chunk.id + assert current_tool_call_id is not None # noqa: S101 + assert current_tool_call_chunk.name is not None # noqa: S101 + function_schema = select_function_schema( + self._function_schemas, current_tool_call_chunk.name + ) + if function_schema is None: + assert current_tool_call_id is not None # noqa: S101 + raise UnknownToolError( + output_message=self._state.current_message_snapshot, + tool_call_id=current_tool_call_id, + tool_name=current_tool_call_chunk.name, + ) + try: + yield function_schema.parse_args( + self._tool_call( + chain([current_tool_call_chunk], tool_calls_stream), + tool_call_ref, + current_tool_call_id, + ) + ) + except ValidationError as e: + assert current_tool_call_id is not None # noqa: S101 + raise ToolSchemaParseError( + output_message=self._state.current_message_snapshot, + tool_call_id=current_tool_call_id, + validation_error=e, + ) from e + elif new_current_item := next(stream, None): + current_item_ref.append(new_current_item) + + @property + def usage_ref(self) -> list[Usage]: + return self._state.usage_ref + + +class AsyncOutputStream(Generic[ItemT, OutputT]): + """Async version of `OutputStream`.""" + + def __init__( + self, + stream: AsyncIterator[ItemT], + function_schemas: Iterable[AsyncFunctionSchema[OutputT]], + parser: StreamParser[ItemT], + state: StreamState[ItemT], + ): + self._stream = stream + self._function_schemas = function_schemas + self._parser = parser + self._state = state + + self._iterator = self.__stream__() + + async def __anext__(self) -> AsyncStreamedStr | OutputT: + return await self._iterator.__anext__() + + async def __aiter__(self) -> AsyncIterator[AsyncStreamedStr | OutputT]: + async for item in self._iterator: + yield item + + async def _streamed_str( + self, stream: AsyncIterator[ItemT], current_item_ref: list[ItemT] + ) -> AsyncIterator[str]: + async for item in stream: + if content := self._parser.get_content(item): + yield content + if self._parser.is_tool_call(item): + # TODO: Check if output types allow for early return + assert not current_item_ref # noqa: S101 + current_item_ref.append(item) + return + + async def _tool_call( + self, + stream: AsyncIterator[FunctionCallChunk], + current_tool_call_ref: list[FunctionCallChunk], + current_tool_call_id: str, + ) -> AsyncIterator[str]: + async for item in stream: + if item.id and item.id != current_tool_call_id: + # TODO: Check if output types allow for early return + assert not current_tool_call_ref # noqa: S101 + current_tool_call_ref.append(item) + return + if item.args: + yield item.args + + async def __stream__(self) -> AsyncIterator[AsyncStreamedStr | OutputT]: + stream = aapply(self._state.update, self._stream) + current_item_ref = [await anext(stream)] + while current_item_ref: + current_item = current_item_ref.pop() + if self._parser.is_content(current_item): + stream = achain(async_iter([current_item]), stream) + yield AsyncStreamedStr(self._streamed_str(stream, current_item_ref)) + elif self._parser.is_tool_call(current_item): + tool_calls_stream = ( + tool_call_chunk + async for item in achain(async_iter([current_item]), stream) + for tool_call_chunk in self._parser.iter_tool_calls(item) + ) + tool_call_ref = [await anext(tool_calls_stream)] + while tool_call_ref: + current_tool_call_chunk = tool_call_ref.pop() + current_tool_call_id = current_tool_call_chunk.id + assert current_tool_call_id is not None # noqa: S101 + assert current_tool_call_chunk.name is not None # noqa: S101 + function_schema = select_function_schema( + self._function_schemas, current_tool_call_chunk.name + ) + if function_schema is None: + assert current_tool_call_id is not None # noqa: S101 + raise UnknownToolError( + output_message=self._state.current_message_snapshot, + tool_call_id=current_tool_call_id, + tool_name=current_tool_call_chunk.name, + ) + try: + yield await function_schema.aparse_args( + self._tool_call( + achain( + async_iter([current_tool_call_chunk]), + tool_calls_stream, + ), + tool_call_ref, + current_tool_call_id, + ) + ) + except ValidationError as e: + assert current_tool_call_id is not None # noqa: S101 + raise ToolSchemaParseError( + output_message=self._state.current_message_snapshot, + tool_call_id=current_tool_call_id, + validation_error=e, + ) from e + elif new_current_item := await anext(stream, None): + current_item_ref.append(new_current_item) + + @property + def usage_ref(self) -> list[Usage]: + return self._state.usage_ref diff --git a/src/magentic/prompt_chain.py b/src/magentic/prompt_chain.py index dda2e7fc..ab9b7fa8 100644 --- a/src/magentic/prompt_chain.py +++ b/src/magentic/prompt_chain.py @@ -37,7 +37,8 @@ def decorator(func: Callable[P, R]) -> Callable[P, R]: async_prompt_function = AsyncPromptFunction[P, Any]( name=func.__name__, parameters=list(func_signature.parameters.values()), - return_type=func_signature.return_annotation, + # TODO: Also allow ParallelFunctionCall. Support this more neatly + return_type=func_signature.return_annotation | FunctionCall, # type: ignore[arg-type,unused-ignore] template=template, functions=functions, model=model, @@ -70,7 +71,8 @@ async def awrapper(*args: P.args, **kwargs: P.kwargs) -> Any: prompt_function = PromptFunction[P, R]( name=func.__name__, parameters=list(func_signature.parameters.values()), - return_type=func_signature.return_annotation, + # TODO: Also allow ParallelFunctionCall. Support this more neatly + return_type=func_signature.return_annotation | FunctionCall, # type: ignore[arg-type,unused-ignore] template=template, functions=functions, model=model, diff --git a/src/magentic/streaming.py b/src/magentic/streaming.py index c3154167..e87af083 100644 --- a/src/magentic/streaming.py +++ b/src/magentic/streaming.py @@ -225,6 +225,8 @@ async def __aiter__(self) -> AsyncIterator[T]: yield item +# TODO: Add close method to close the underlying stream if chunks is a stream +# TODO: Make it a context manager to automatically close class StreamedStr(Iterable[str]): """A string that is generated in chunks.""" diff --git a/tests/cassettes/test_prompt_chain/test_async_prompt_chain.yaml b/tests/cassettes/test_prompt_chain/test_async_prompt_chain.yaml index ed49702e..837bded5 100644 --- a/tests/cassettes/test_prompt_chain/test_async_prompt_chain.yaml +++ b/tests/cassettes/test_prompt_chain/test_async_prompt_chain.yaml @@ -42,28 +42,43 @@ interactions: uri: https://api.openai.com/v1/chat/completions response: body: - string: 'data: {"id":"chatcmpl-AWvDJeef7lugHCWP856cRKsxN6wzr","object":"chat.completion.chunk","created":1732409789,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_ToRc3P23Nq9mz68k7QXKvG7y","type":"function","function":{"name":"get_current_weather","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} + string: 'data: {"id":"chatcmpl-AY7NvhQY8twSiXbZnXoHygtw9OZvK","object":"chat.completion.chunk","created":1732694903,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_OdGRePgCvnJSeHHjYuviwBrB","type":"function","function":{"name":"get_current_weather","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AWvDJeef7lugHCWP856cRKsxN6wzr","object":"chat.completion.chunk","created":1732409789,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AY7NvhQY8twSiXbZnXoHygtw9OZvK","object":"chat.completion.chunk","created":1732694903,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AWvDJeef7lugHCWP856cRKsxN6wzr","object":"chat.completion.chunk","created":1732409789,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"location"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AY7NvhQY8twSiXbZnXoHygtw9OZvK","object":"chat.completion.chunk","created":1732694903,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"location"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AWvDJeef7lugHCWP856cRKsxN6wzr","object":"chat.completion.chunk","created":1732409789,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AY7NvhQY8twSiXbZnXoHygtw9OZvK","object":"chat.completion.chunk","created":1732694903,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AWvDJeef7lugHCWP856cRKsxN6wzr","object":"chat.completion.chunk","created":1732409789,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Boston"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AY7NvhQY8twSiXbZnXoHygtw9OZvK","object":"chat.completion.chunk","created":1732694903,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Boston"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AWvDJeef7lugHCWP856cRKsxN6wzr","object":"chat.completion.chunk","created":1732409789,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AY7NvhQY8twSiXbZnXoHygtw9OZvK","object":"chat.completion.chunk","created":1732694903,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AWvDJeef7lugHCWP856cRKsxN6wzr","object":"chat.completion.chunk","created":1732409789,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null} + data: {"id":"chatcmpl-AY7NvhQY8twSiXbZnXoHygtw9OZvK","object":"chat.completion.chunk","created":1732694903,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"unit"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AWvDJeef7lugHCWP856cRKsxN6wzr","object":"chat.completion.chunk","created":1732409789,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[],"usage":{"prompt_tokens":70,"completion_tokens":15,"total_tokens":85,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} + data: {"id":"chatcmpl-AY7NvhQY8twSiXbZnXoHygtw9OZvK","object":"chat.completion.chunk","created":1732694903,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + + + data: {"id":"chatcmpl-AY7NvhQY8twSiXbZnXoHygtw9OZvK","object":"chat.completion.chunk","created":1732694903,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"fahren"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + + + data: {"id":"chatcmpl-AY7NvhQY8twSiXbZnXoHygtw9OZvK","object":"chat.completion.chunk","created":1732694903,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"heit"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + + + data: {"id":"chatcmpl-AY7NvhQY8twSiXbZnXoHygtw9OZvK","object":"chat.completion.chunk","created":1732694903,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + + + data: {"id":"chatcmpl-AY7NvhQY8twSiXbZnXoHygtw9OZvK","object":"chat.completion.chunk","created":1732694903,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null} + + + data: {"id":"chatcmpl-AY7NvhQY8twSiXbZnXoHygtw9OZvK","object":"chat.completion.chunk","created":1732694903,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_831e067d82","choices":[],"usage":{"prompt_tokens":70,"completion_tokens":20,"total_tokens":90,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} data: [DONE] @@ -74,13 +89,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8e7570824d451742-SJC + - 8e90a14a0c8d22d2-SJC Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Sun, 24 Nov 2024 00:56:30 GMT + - Wed, 27 Nov 2024 08:08:23 GMT Server: - cloudflare Transfer-Encoding: @@ -92,7 +107,7 @@ interactions: alt-svc: - h3=":443"; ma=86400 openai-processing-ms: - - '429' + - '369' openai-version: - '2020-10-01' strict-transport-security: @@ -104,13 +119,13 @@ interactions: x-ratelimit-remaining-requests: - '499' x-ratelimit-remaining-tokens: - - '29961' + - '29974' x-ratelimit-reset-requests: - 120ms x-ratelimit-reset-tokens: - - 77ms + - 52ms x-request-id: - - req_82bb4d277af45ba184f65ba214a706e8 + - req_29acff04284aaa4e4342f2af54d4a4b2 status: code: 200 message: OK @@ -118,8 +133,8 @@ interactions: body: '{"messages": [{"role": "user", "content": "What''s the weather like in Boston?"}, {"role": "assistant", "content": null, "tool_calls": [{"id": "000000000", "type": "function", "function": {"name": "get_current_weather", "arguments": - "{\"location\":\"Boston\"}"}}]}, {"role": "tool", "tool_call_id": "000000000", - "content": "{\"location\":\"Boston\",\"temperature\":\"72\",\"unit\":\"fahrenheit\",\"forecast\":[\"sunny\",\"windy\"]}"}], + "{\"location\":\"Boston\",\"unit\":\"fahrenheit\"}"}}]}, {"role": "tool", "tool_call_id": + "000000000", "content": "{\"location\":\"Boston\",\"temperature\":\"72\",\"unit\":\"fahrenheit\",\"forecast\":[\"sunny\",\"windy\"]}"}], "model": "gpt-4o", "parallel_tool_calls": false, "stream": true, "stream_options": {"include_usage": true}, "tools": [{"type": "function", "function": {"name": "get_current_weather", "parameters": {"properties": {"location": {"title": "Location"}, @@ -133,7 +148,7 @@ interactions: connection: - keep-alive content-length: - - '845' + - '869' content-type: - application/json host: @@ -160,46 +175,46 @@ interactions: uri: https://api.openai.com/v1/chat/completions response: body: - string: "data: {\"id\":\"chatcmpl-AWvDKT3iXncr2YaxeGAtmHxL7mBvO\",\"object\":\"chat.completion.chunk\",\"created\":1732409790,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"\",\"refusal\":null},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDKT3iXncr2YaxeGAtmHxL7mBvO\",\"object\":\"chat.completion.chunk\",\"created\":1732409790,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"The\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDKT3iXncr2YaxeGAtmHxL7mBvO\",\"object\":\"chat.completion.chunk\",\"created\":1732409790,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + string: "data: {\"id\":\"chatcmpl-AY7Nw0Ld9DsdOxrbVxns7DBN8eA9a\",\"object\":\"chat.completion.chunk\",\"created\":1732694904,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"\",\"refusal\":null},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: + {\"id\":\"chatcmpl-AY7Nw0Ld9DsdOxrbVxns7DBN8eA9a\",\"object\":\"chat.completion.chunk\",\"created\":1732694904,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"The\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: + {\"id\":\"chatcmpl-AY7Nw0Ld9DsdOxrbVxns7DBN8eA9a\",\"object\":\"chat.completion.chunk\",\"created\":1732694904,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" current\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDKT3iXncr2YaxeGAtmHxL7mBvO\",\"object\":\"chat.completion.chunk\",\"created\":1732409790,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + {\"id\":\"chatcmpl-AY7Nw0Ld9DsdOxrbVxns7DBN8eA9a\",\"object\":\"chat.completion.chunk\",\"created\":1732694904,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" weather\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDKT3iXncr2YaxeGAtmHxL7mBvO\",\"object\":\"chat.completion.chunk\",\"created\":1732409790,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + {\"id\":\"chatcmpl-AY7Nw0Ld9DsdOxrbVxns7DBN8eA9a\",\"object\":\"chat.completion.chunk\",\"created\":1732694904,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" in\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDKT3iXncr2YaxeGAtmHxL7mBvO\",\"object\":\"chat.completion.chunk\",\"created\":1732409790,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + {\"id\":\"chatcmpl-AY7Nw0Ld9DsdOxrbVxns7DBN8eA9a\",\"object\":\"chat.completion.chunk\",\"created\":1732694904,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" Boston\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDKT3iXncr2YaxeGAtmHxL7mBvO\",\"object\":\"chat.completion.chunk\",\"created\":1732409790,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + {\"id\":\"chatcmpl-AY7Nw0Ld9DsdOxrbVxns7DBN8eA9a\",\"object\":\"chat.completion.chunk\",\"created\":1732694904,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" is\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDKT3iXncr2YaxeGAtmHxL7mBvO\",\"object\":\"chat.completion.chunk\",\"created\":1732409790,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" - \"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: {\"id\":\"chatcmpl-AWvDKT3iXncr2YaxeGAtmHxL7mBvO\",\"object\":\"chat.completion.chunk\",\"created\":1732409790,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"72\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDKT3iXncr2YaxeGAtmHxL7mBvO\",\"object\":\"chat.completion.chunk\",\"created\":1732409790,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"\xB0F\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDKT3iXncr2YaxeGAtmHxL7mBvO\",\"object\":\"chat.completion.chunk\",\"created\":1732409790,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + {\"id\":\"chatcmpl-AY7Nw0Ld9DsdOxrbVxns7DBN8eA9a\",\"object\":\"chat.completion.chunk\",\"created\":1732694904,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + \"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: {\"id\":\"chatcmpl-AY7Nw0Ld9DsdOxrbVxns7DBN8eA9a\",\"object\":\"chat.completion.chunk\",\"created\":1732694904,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"72\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: + {\"id\":\"chatcmpl-AY7Nw0Ld9DsdOxrbVxns7DBN8eA9a\",\"object\":\"chat.completion.chunk\",\"created\":1732694904,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"\xB0F\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: + {\"id\":\"chatcmpl-AY7Nw0Ld9DsdOxrbVxns7DBN8eA9a\",\"object\":\"chat.completion.chunk\",\"created\":1732694904,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" with\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDKT3iXncr2YaxeGAtmHxL7mBvO\",\"object\":\"chat.completion.chunk\",\"created\":1732409790,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + {\"id\":\"chatcmpl-AY7Nw0Ld9DsdOxrbVxns7DBN8eA9a\",\"object\":\"chat.completion.chunk\",\"created\":1732694904,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" sunny\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDKT3iXncr2YaxeGAtmHxL7mBvO\",\"object\":\"chat.completion.chunk\",\"created\":1732409790,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + {\"id\":\"chatcmpl-AY7Nw0Ld9DsdOxrbVxns7DBN8eA9a\",\"object\":\"chat.completion.chunk\",\"created\":1732694904,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" and\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDKT3iXncr2YaxeGAtmHxL7mBvO\",\"object\":\"chat.completion.chunk\",\"created\":1732409790,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + {\"id\":\"chatcmpl-AY7Nw0Ld9DsdOxrbVxns7DBN8eA9a\",\"object\":\"chat.completion.chunk\",\"created\":1732694904,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" windy\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDKT3iXncr2YaxeGAtmHxL7mBvO\",\"object\":\"chat.completion.chunk\",\"created\":1732409790,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + {\"id\":\"chatcmpl-AY7Nw0Ld9DsdOxrbVxns7DBN8eA9a\",\"object\":\"chat.completion.chunk\",\"created\":1732694904,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" conditions\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDKT3iXncr2YaxeGAtmHxL7mBvO\",\"object\":\"chat.completion.chunk\",\"created\":1732409790,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\".\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDKT3iXncr2YaxeGAtmHxL7mBvO\",\"object\":\"chat.completion.chunk\",\"created\":1732409790,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{},\"logprobs\":null,\"finish_reason\":\"stop\"}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDKT3iXncr2YaxeGAtmHxL7mBvO\",\"object\":\"chat.completion.chunk\",\"created\":1732409790,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[],\"usage\":{\"prompt_tokens\":117,\"completion_tokens\":16,\"total_tokens\":133,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"audio_tokens\":0,\"accepted_prediction_tokens\":0,\"rejected_prediction_tokens\":0}}}\n\ndata: + {\"id\":\"chatcmpl-AY7Nw0Ld9DsdOxrbVxns7DBN8eA9a\",\"object\":\"chat.completion.chunk\",\"created\":1732694904,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\".\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: + {\"id\":\"chatcmpl-AY7Nw0Ld9DsdOxrbVxns7DBN8eA9a\",\"object\":\"chat.completion.chunk\",\"created\":1732694904,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{},\"logprobs\":null,\"finish_reason\":\"stop\"}],\"usage\":null}\n\ndata: + {\"id\":\"chatcmpl-AY7Nw0Ld9DsdOxrbVxns7DBN8eA9a\",\"object\":\"chat.completion.chunk\",\"created\":1732694904,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[],\"usage\":{\"prompt_tokens\":122,\"completion_tokens\":16,\"total_tokens\":138,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"audio_tokens\":0,\"accepted_prediction_tokens\":0,\"rejected_prediction_tokens\":0}}}\n\ndata: [DONE]\n\n" headers: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8e757086cd80d00d-SJC + - 8e90a14fcc6b67c7-SJC Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Sun, 24 Nov 2024 00:56:30 GMT + - Wed, 27 Nov 2024 08:08:24 GMT Server: - cloudflare Transfer-Encoding: @@ -211,7 +226,7 @@ interactions: alt-svc: - h3=":443"; ma=86400 openai-processing-ms: - - '217' + - '255' openai-version: - '2020-10-01' strict-transport-security: @@ -229,7 +244,7 @@ interactions: x-ratelimit-reset-tokens: - 100ms x-request-id: - - req_b48ce435e67d7bbfadffa112e5647e88 + - req_700e5a9d06cbc5721c78aa22d0fbce6d status: code: 200 message: OK diff --git a/tests/cassettes/test_prompt_chain/test_prompt_chain.yaml b/tests/cassettes/test_prompt_chain/test_prompt_chain.yaml index e8cc72c7..128a2f8d 100644 --- a/tests/cassettes/test_prompt_chain/test_prompt_chain.yaml +++ b/tests/cassettes/test_prompt_chain/test_prompt_chain.yaml @@ -42,28 +42,28 @@ interactions: uri: https://api.openai.com/v1/chat/completions response: body: - string: 'data: {"id":"chatcmpl-AWvDIAUsQhMsrZNf6Y80Ycv2FfndY","object":"chat.completion.chunk","created":1732409788,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_hkxs9H7xFCzJ4aZkX3a1cCri","type":"function","function":{"name":"get_current_weather","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} + string: 'data: {"id":"chatcmpl-AY7NtLye868hzjFznwHUkCMpHwVn6","object":"chat.completion.chunk","created":1732694901,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_P3DrUvqprybs0ewD94G0ate8","type":"function","function":{"name":"get_current_weather","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AWvDIAUsQhMsrZNf6Y80Ycv2FfndY","object":"chat.completion.chunk","created":1732409788,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AY7NtLye868hzjFznwHUkCMpHwVn6","object":"chat.completion.chunk","created":1732694901,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AWvDIAUsQhMsrZNf6Y80Ycv2FfndY","object":"chat.completion.chunk","created":1732409788,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"location"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AY7NtLye868hzjFznwHUkCMpHwVn6","object":"chat.completion.chunk","created":1732694901,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"location"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AWvDIAUsQhMsrZNf6Y80Ycv2FfndY","object":"chat.completion.chunk","created":1732409788,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AY7NtLye868hzjFznwHUkCMpHwVn6","object":"chat.completion.chunk","created":1732694901,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AWvDIAUsQhMsrZNf6Y80Ycv2FfndY","object":"chat.completion.chunk","created":1732409788,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Boston"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AY7NtLye868hzjFznwHUkCMpHwVn6","object":"chat.completion.chunk","created":1732694901,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Boston"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AWvDIAUsQhMsrZNf6Y80Ycv2FfndY","object":"chat.completion.chunk","created":1732409788,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AY7NtLye868hzjFznwHUkCMpHwVn6","object":"chat.completion.chunk","created":1732694901,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AWvDIAUsQhMsrZNf6Y80Ycv2FfndY","object":"chat.completion.chunk","created":1732409788,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null} + data: {"id":"chatcmpl-AY7NtLye868hzjFznwHUkCMpHwVn6","object":"chat.completion.chunk","created":1732694901,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}],"usage":null} - data: {"id":"chatcmpl-AWvDIAUsQhMsrZNf6Y80Ycv2FfndY","object":"chat.completion.chunk","created":1732409788,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[],"usage":{"prompt_tokens":70,"completion_tokens":15,"total_tokens":85,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} + data: {"id":"chatcmpl-AY7NtLye868hzjFznwHUkCMpHwVn6","object":"chat.completion.chunk","created":1732694901,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[],"usage":{"prompt_tokens":70,"completion_tokens":15,"total_tokens":85,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} data: [DONE] @@ -74,13 +74,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8e75707a1cf29e6a-SJC + - 8e90a13d0e5d17e6-SJC Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Sun, 24 Nov 2024 00:56:28 GMT + - Wed, 27 Nov 2024 08:08:22 GMT Server: - cloudflare Transfer-Encoding: @@ -92,7 +92,7 @@ interactions: alt-svc: - h3=":443"; ma=86400 openai-processing-ms: - - '315' + - '681' openai-version: - '2020-10-01' strict-transport-security: @@ -110,7 +110,7 @@ interactions: x-ratelimit-reset-tokens: - 52ms x-request-id: - - req_d2ba77cde731e6a09799f02ea452a5d3 + - req_0d1d0bcbe79f8af2fa00aab53e512b43 status: code: 200 message: OK @@ -160,46 +160,46 @@ interactions: uri: https://api.openai.com/v1/chat/completions response: body: - string: "data: {\"id\":\"chatcmpl-AWvDJw8GmT0OyqR7pIJ62gwE1yijz\",\"object\":\"chat.completion.chunk\",\"created\":1732409789,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"\",\"refusal\":null},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDJw8GmT0OyqR7pIJ62gwE1yijz\",\"object\":\"chat.completion.chunk\",\"created\":1732409789,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"The\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDJw8GmT0OyqR7pIJ62gwE1yijz\",\"object\":\"chat.completion.chunk\",\"created\":1732409789,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + string: "data: {\"id\":\"chatcmpl-AY7NutD05eI0kpgHRMnrDqTtYnvMW\",\"object\":\"chat.completion.chunk\",\"created\":1732694902,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_a7d06e42a7\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"\",\"refusal\":null},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: + {\"id\":\"chatcmpl-AY7NutD05eI0kpgHRMnrDqTtYnvMW\",\"object\":\"chat.completion.chunk\",\"created\":1732694902,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_a7d06e42a7\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"The\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: + {\"id\":\"chatcmpl-AY7NutD05eI0kpgHRMnrDqTtYnvMW\",\"object\":\"chat.completion.chunk\",\"created\":1732694902,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_a7d06e42a7\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" current\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDJw8GmT0OyqR7pIJ62gwE1yijz\",\"object\":\"chat.completion.chunk\",\"created\":1732409789,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + {\"id\":\"chatcmpl-AY7NutD05eI0kpgHRMnrDqTtYnvMW\",\"object\":\"chat.completion.chunk\",\"created\":1732694902,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_a7d06e42a7\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" weather\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDJw8GmT0OyqR7pIJ62gwE1yijz\",\"object\":\"chat.completion.chunk\",\"created\":1732409789,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + {\"id\":\"chatcmpl-AY7NutD05eI0kpgHRMnrDqTtYnvMW\",\"object\":\"chat.completion.chunk\",\"created\":1732694902,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_a7d06e42a7\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" in\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDJw8GmT0OyqR7pIJ62gwE1yijz\",\"object\":\"chat.completion.chunk\",\"created\":1732409789,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + {\"id\":\"chatcmpl-AY7NutD05eI0kpgHRMnrDqTtYnvMW\",\"object\":\"chat.completion.chunk\",\"created\":1732694902,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_a7d06e42a7\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" Boston\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDJw8GmT0OyqR7pIJ62gwE1yijz\",\"object\":\"chat.completion.chunk\",\"created\":1732409789,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + {\"id\":\"chatcmpl-AY7NutD05eI0kpgHRMnrDqTtYnvMW\",\"object\":\"chat.completion.chunk\",\"created\":1732694902,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_a7d06e42a7\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" is\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDJw8GmT0OyqR7pIJ62gwE1yijz\",\"object\":\"chat.completion.chunk\",\"created\":1732409789,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" - \"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: {\"id\":\"chatcmpl-AWvDJw8GmT0OyqR7pIJ62gwE1yijz\",\"object\":\"chat.completion.chunk\",\"created\":1732409789,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"72\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDJw8GmT0OyqR7pIJ62gwE1yijz\",\"object\":\"chat.completion.chunk\",\"created\":1732409789,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"\xB0F\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDJw8GmT0OyqR7pIJ62gwE1yijz\",\"object\":\"chat.completion.chunk\",\"created\":1732409789,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + {\"id\":\"chatcmpl-AY7NutD05eI0kpgHRMnrDqTtYnvMW\",\"object\":\"chat.completion.chunk\",\"created\":1732694902,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_a7d06e42a7\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + \"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: {\"id\":\"chatcmpl-AY7NutD05eI0kpgHRMnrDqTtYnvMW\",\"object\":\"chat.completion.chunk\",\"created\":1732694902,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_a7d06e42a7\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"72\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: + {\"id\":\"chatcmpl-AY7NutD05eI0kpgHRMnrDqTtYnvMW\",\"object\":\"chat.completion.chunk\",\"created\":1732694902,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_a7d06e42a7\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"\xB0F\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: + {\"id\":\"chatcmpl-AY7NutD05eI0kpgHRMnrDqTtYnvMW\",\"object\":\"chat.completion.chunk\",\"created\":1732694902,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_a7d06e42a7\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" with\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDJw8GmT0OyqR7pIJ62gwE1yijz\",\"object\":\"chat.completion.chunk\",\"created\":1732409789,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + {\"id\":\"chatcmpl-AY7NutD05eI0kpgHRMnrDqTtYnvMW\",\"object\":\"chat.completion.chunk\",\"created\":1732694902,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_a7d06e42a7\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" sunny\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDJw8GmT0OyqR7pIJ62gwE1yijz\",\"object\":\"chat.completion.chunk\",\"created\":1732409789,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + {\"id\":\"chatcmpl-AY7NutD05eI0kpgHRMnrDqTtYnvMW\",\"object\":\"chat.completion.chunk\",\"created\":1732694902,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_a7d06e42a7\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" and\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDJw8GmT0OyqR7pIJ62gwE1yijz\",\"object\":\"chat.completion.chunk\",\"created\":1732409789,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + {\"id\":\"chatcmpl-AY7NutD05eI0kpgHRMnrDqTtYnvMW\",\"object\":\"chat.completion.chunk\",\"created\":1732694902,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_a7d06e42a7\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" windy\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDJw8GmT0OyqR7pIJ62gwE1yijz\",\"object\":\"chat.completion.chunk\",\"created\":1732409789,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" + {\"id\":\"chatcmpl-AY7NutD05eI0kpgHRMnrDqTtYnvMW\",\"object\":\"chat.completion.chunk\",\"created\":1732694902,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_a7d06e42a7\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" conditions\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDJw8GmT0OyqR7pIJ62gwE1yijz\",\"object\":\"chat.completion.chunk\",\"created\":1732409789,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\".\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDJw8GmT0OyqR7pIJ62gwE1yijz\",\"object\":\"chat.completion.chunk\",\"created\":1732409789,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[{\"index\":0,\"delta\":{},\"logprobs\":null,\"finish_reason\":\"stop\"}],\"usage\":null}\n\ndata: - {\"id\":\"chatcmpl-AWvDJw8GmT0OyqR7pIJ62gwE1yijz\",\"object\":\"chat.completion.chunk\",\"created\":1732409789,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_7f6be3efb0\",\"choices\":[],\"usage\":{\"prompt_tokens\":117,\"completion_tokens\":16,\"total_tokens\":133,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"audio_tokens\":0,\"accepted_prediction_tokens\":0,\"rejected_prediction_tokens\":0}}}\n\ndata: + {\"id\":\"chatcmpl-AY7NutD05eI0kpgHRMnrDqTtYnvMW\",\"object\":\"chat.completion.chunk\",\"created\":1732694902,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_a7d06e42a7\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\".\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null}\n\ndata: + {\"id\":\"chatcmpl-AY7NutD05eI0kpgHRMnrDqTtYnvMW\",\"object\":\"chat.completion.chunk\",\"created\":1732694902,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_a7d06e42a7\",\"choices\":[{\"index\":0,\"delta\":{},\"logprobs\":null,\"finish_reason\":\"stop\"}],\"usage\":null}\n\ndata: + {\"id\":\"chatcmpl-AY7NutD05eI0kpgHRMnrDqTtYnvMW\",\"object\":\"chat.completion.chunk\",\"created\":1732694902,\"model\":\"gpt-4o-2024-08-06\",\"system_fingerprint\":\"fp_a7d06e42a7\",\"choices\":[],\"usage\":{\"prompt_tokens\":117,\"completion_tokens\":16,\"total_tokens\":133,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"audio_tokens\":0,\"accepted_prediction_tokens\":0,\"rejected_prediction_tokens\":0}}}\n\ndata: [DONE]\n\n" headers: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8e75707daabacf2e-SJC + - 8e90a1446b19270c-SJC Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Sun, 24 Nov 2024 00:56:29 GMT + - Wed, 27 Nov 2024 08:08:22 GMT Server: - cloudflare Transfer-Encoding: @@ -211,7 +211,7 @@ interactions: alt-svc: - h3=":443"; ma=86400 openai-processing-ms: - - '358' + - '421' openai-version: - '2020-10-01' strict-transport-security: @@ -229,7 +229,7 @@ interactions: x-ratelimit-reset-tokens: - 100ms x-request-id: - - req_646939d1b32780ef61665e5b65261787 + - req_039b14c0ae6a88154cd0e379aeae92e0 status: code: 200 message: OK diff --git a/tests/cassettes/test_prompt_function/test_async_decorator_max_retries.yaml b/tests/cassettes/test_prompt_function/test_async_decorator_max_retries.yaml index a20fa538..6776a4e1 100644 --- a/tests/cassettes/test_prompt_function/test_async_decorator_max_retries.yaml +++ b/tests/cassettes/test_prompt_function/test_async_decorator_max_retries.yaml @@ -41,28 +41,28 @@ interactions: uri: https://api.openai.com/v1/chat/completions response: body: - string: 'data: {"id":"chatcmpl-AU9UbB4ld1146U4QhHhxP4F02h3eV","object":"chat.completion.chunk","created":1731749693,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_Uiz13O1iMw5tob2yIDXaRHDH","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} + string: 'data: {"id":"chatcmpl-AXM5kjh54ArE89qKZKNsCik5EZIEt","object":"chat.completion.chunk","created":1732513108,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_6Dk7MgfrU5OMIvVdcxiJ9TLE","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UbB4ld1146U4QhHhxP4F02h3eV","object":"chat.completion.chunk","created":1731749693,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5kjh54ArE89qKZKNsCik5EZIEt","object":"chat.completion.chunk","created":1732513108,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UbB4ld1146U4QhHhxP4F02h3eV","object":"chat.completion.chunk","created":1731749693,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5kjh54ArE89qKZKNsCik5EZIEt","object":"chat.completion.chunk","created":1732513108,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UbB4ld1146U4QhHhxP4F02h3eV","object":"chat.completion.chunk","created":1731749693,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5kjh54ArE89qKZKNsCik5EZIEt","object":"chat.completion.chunk","created":1732513108,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UbB4ld1146U4QhHhxP4F02h3eV","object":"chat.completion.chunk","created":1731749693,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Japan"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5kjh54ArE89qKZKNsCik5EZIEt","object":"chat.completion.chunk","created":1732513108,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Australia"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UbB4ld1146U4QhHhxP4F02h3eV","object":"chat.completion.chunk","created":1731749693,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5kjh54ArE89qKZKNsCik5EZIEt","object":"chat.completion.chunk","created":1732513108,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UbB4ld1146U4QhHhxP4F02h3eV","object":"chat.completion.chunk","created":1731749693,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} + data: {"id":"chatcmpl-AXM5kjh54ArE89qKZKNsCik5EZIEt","object":"chat.completion.chunk","created":1732513108,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} - data: {"id":"chatcmpl-AU9UbB4ld1146U4QhHhxP4F02h3eV","object":"chat.completion.chunk","created":1731749693,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[],"usage":{"prompt_tokens":53,"completion_tokens":5,"total_tokens":58,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} + data: {"id":"chatcmpl-AXM5kjh54ArE89qKZKNsCik5EZIEt","object":"chat.completion.chunk","created":1732513108,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[],"usage":{"prompt_tokens":53,"completion_tokens":5,"total_tokens":58,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} data: [DONE] @@ -73,13 +73,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8e367cdc792b2523-SJC + - 8e7f4af0edbf238d-SJC Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Sat, 16 Nov 2024 09:34:53 GMT + - Mon, 25 Nov 2024 05:38:28 GMT Server: - cloudflare Transfer-Encoding: @@ -91,7 +91,7 @@ interactions: alt-svc: - h3=":443"; ma=86400 openai-processing-ms: - - '231' + - '205' openai-version: - '2020-10-01' strict-transport-security: @@ -109,23 +109,24 @@ interactions: x-ratelimit-reset-tokens: - 44ms x-request-id: - - req_36b6885837304c95255dc0e243ddd0bc + - req_5df2a7cf8b9d982084c0c588bcce800d status: code: 200 message: OK - request: - body: '{"messages": [{"role": "user", "content": "Return a country."}, {"role": - "assistant", "content": null, "tool_calls": [{"id": "call_Uiz13O1iMw5tob2yIDXaRHDH", - "type": "function", "function": {"name": "return_country", "arguments": "{\"name\":\"Japan\"}"}}]}, - {"role": "tool", "tool_call_id": "call_Uiz13O1iMw5tob2yIDXaRHDH", "content": - "1 validation error for Country\nname\n Value error, Country must be Ireland. - [type=value_error, input_value=''Japan'', input_type=str]\n For further information - visit https://errors.pydantic.dev/2.9/v/value_error"}], "model": "gpt-4o", "parallel_tool_calls": - false, "stream": true, "stream_options": {"include_usage": true}, "tool_choice": - {"type": "function", "function": {"name": "return_country"}}, "tools": [{"type": - "function", "function": {"name": "return_country", "parameters": {"properties": - {"name": {"title": "Name", "type": "string"}}, "required": ["name"], "type": - "object"}}}]}' + body: '{"messages": [{"role": "user", "content": "Return a country."}, {"content": + null, "refusal": null, "role": "assistant", "audio": null, "function_call": + null, "tool_calls": [{"id": "call_6Dk7MgfrU5OMIvVdcxiJ9TLE", "function": {"arguments": + "{\"name\":\"Australia\"}", "name": "return_country", "parsed_arguments": null}, + "type": "function", "index": 0}], "parsed": null}, {"role": "tool", "tool_call_id": + "call_6Dk7MgfrU5OMIvVdcxiJ9TLE", "content": "1 validation error for Country\nname\n Value + error, Country must be Ireland. [type=value_error, input_value=''Australia'', + input_type=str]\n For further information visit https://errors.pydantic.dev/2.9/v/value_error"}], + "model": "gpt-4o", "parallel_tool_calls": false, "stream": true, "stream_options": + {"include_usage": true}, "tool_choice": {"type": "function", "function": {"name": + "return_country"}}, "tools": [{"type": "function", "function": {"name": "return_country", + "parameters": {"properties": {"name": {"title": "Name", "type": "string"}}, + "required": ["name"], "type": "object"}}}]}' headers: accept: - application/json @@ -134,12 +135,9 @@ interactions: connection: - keep-alive content-length: - - '929' + - '1046' content-type: - application/json - cookie: - - __cf_bm=wEsL6ULd.xp6gCoeJPmKaXwe3SOIEpJ2OD7N7AW5Uws-1731749693-1.0.1.1-ub_GAbKgRsGOITlv_88OsV4ivL8UZhOb2zb2IxLxtM4Z6zT3bROk5hV_1ZcPL.d.KcwDZg9Ns_qofiVGmrMiuQ; - _cfuvid=_7gaoOsQ2EivHbO2o7pDXxdovG6ZmrHdC3szn.mJVww-1731749693141-0.0.1.1-604800000 host: - api.openai.com user-agent: @@ -164,28 +162,28 @@ interactions: uri: https://api.openai.com/v1/chat/completions response: body: - string: 'data: {"id":"chatcmpl-AU9UbpuvQHeG32Dr7WUawf4oshZpp","object":"chat.completion.chunk","created":1731749693,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_ifXs1dcqtJy3GNJ0UpW7AR8C","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} + string: 'data: {"id":"chatcmpl-AXM5lf2YZmhdFaEv938fU8xSwn5ug","object":"chat.completion.chunk","created":1732513109,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_9492M1JOz1OVPrONfWjISFxN","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UbpuvQHeG32Dr7WUawf4oshZpp","object":"chat.completion.chunk","created":1731749693,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5lf2YZmhdFaEv938fU8xSwn5ug","object":"chat.completion.chunk","created":1732513109,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UbpuvQHeG32Dr7WUawf4oshZpp","object":"chat.completion.chunk","created":1731749693,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5lf2YZmhdFaEv938fU8xSwn5ug","object":"chat.completion.chunk","created":1732513109,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UbpuvQHeG32Dr7WUawf4oshZpp","object":"chat.completion.chunk","created":1731749693,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5lf2YZmhdFaEv938fU8xSwn5ug","object":"chat.completion.chunk","created":1732513109,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UbpuvQHeG32Dr7WUawf4oshZpp","object":"chat.completion.chunk","created":1731749693,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Ireland"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5lf2YZmhdFaEv938fU8xSwn5ug","object":"chat.completion.chunk","created":1732513109,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Ireland"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UbpuvQHeG32Dr7WUawf4oshZpp","object":"chat.completion.chunk","created":1731749693,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5lf2YZmhdFaEv938fU8xSwn5ug","object":"chat.completion.chunk","created":1732513109,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UbpuvQHeG32Dr7WUawf4oshZpp","object":"chat.completion.chunk","created":1731749693,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} + data: {"id":"chatcmpl-AXM5lf2YZmhdFaEv938fU8xSwn5ug","object":"chat.completion.chunk","created":1732513109,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} - data: {"id":"chatcmpl-AU9UbpuvQHeG32Dr7WUawf4oshZpp","object":"chat.completion.chunk","created":1731749693,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[],"usage":{"prompt_tokens":125,"completion_tokens":5,"total_tokens":130,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} + data: {"id":"chatcmpl-AXM5lf2YZmhdFaEv938fU8xSwn5ug","object":"chat.completion.chunk","created":1732513109,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[],"usage":{"prompt_tokens":125,"completion_tokens":5,"total_tokens":130,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} data: [DONE] @@ -196,13 +194,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8e367cdf0aa72523-SJC + - 8e7f4af438da238d-SJC Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Sat, 16 Nov 2024 09:34:53 GMT + - Mon, 25 Nov 2024 05:38:29 GMT Server: - cloudflare Transfer-Encoding: @@ -214,7 +212,7 @@ interactions: alt-svc: - h3=":443"; ma=86400 openai-processing-ms: - - '209' + - '276' openai-version: - '2020-10-01' strict-transport-security: @@ -230,9 +228,9 @@ interactions: x-ratelimit-reset-requests: - 120ms x-ratelimit-reset-tokens: - - 152ms + - 154ms x-request-id: - - req_dc27be5835d770a6829f394a4c68c56c + - req_1c8ba63d5419f12f535bf10ab37d2ddc status: code: 200 message: OK diff --git a/tests/cassettes/test_prompt_function/test_decorator_max_retries.yaml b/tests/cassettes/test_prompt_function/test_decorator_max_retries.yaml index 025b6cbd..1dd495d4 100644 --- a/tests/cassettes/test_prompt_function/test_decorator_max_retries.yaml +++ b/tests/cassettes/test_prompt_function/test_decorator_max_retries.yaml @@ -25,6 +25,8 @@ interactions: - arm64 x-stainless-async: - 'false' + x-stainless-helper-method: + - beta.chat.completions.stream x-stainless-lang: - python x-stainless-os: @@ -41,28 +43,28 @@ interactions: uri: https://api.openai.com/v1/chat/completions response: body: - string: 'data: {"id":"chatcmpl-AU9UZipZ8xDJl7GFgAuLyFkFGnJ9p","object":"chat.completion.chunk","created":1731749691,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_wdEg17NjgwLbqVdKjq6MKLJ1","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} + string: 'data: {"id":"chatcmpl-AXM5jAijInCrqEmlZs6ylqF3hpKe9","object":"chat.completion.chunk","created":1732513107,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_a7d06e42a7","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_Cf3PFZFWlA0He0ZOgneuubd1","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UZipZ8xDJl7GFgAuLyFkFGnJ9p","object":"chat.completion.chunk","created":1731749691,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5jAijInCrqEmlZs6ylqF3hpKe9","object":"chat.completion.chunk","created":1732513107,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_a7d06e42a7","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UZipZ8xDJl7GFgAuLyFkFGnJ9p","object":"chat.completion.chunk","created":1731749691,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5jAijInCrqEmlZs6ylqF3hpKe9","object":"chat.completion.chunk","created":1732513107,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_a7d06e42a7","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UZipZ8xDJl7GFgAuLyFkFGnJ9p","object":"chat.completion.chunk","created":1731749691,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5jAijInCrqEmlZs6ylqF3hpKe9","object":"chat.completion.chunk","created":1732513107,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_a7d06e42a7","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UZipZ8xDJl7GFgAuLyFkFGnJ9p","object":"chat.completion.chunk","created":1731749691,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Canada"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5jAijInCrqEmlZs6ylqF3hpKe9","object":"chat.completion.chunk","created":1732513107,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_a7d06e42a7","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"country"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UZipZ8xDJl7GFgAuLyFkFGnJ9p","object":"chat.completion.chunk","created":1731749691,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5jAijInCrqEmlZs6ylqF3hpKe9","object":"chat.completion.chunk","created":1732513107,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_a7d06e42a7","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UZipZ8xDJl7GFgAuLyFkFGnJ9p","object":"chat.completion.chunk","created":1731749691,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} + data: {"id":"chatcmpl-AXM5jAijInCrqEmlZs6ylqF3hpKe9","object":"chat.completion.chunk","created":1732513107,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_a7d06e42a7","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} - data: {"id":"chatcmpl-AU9UZipZ8xDJl7GFgAuLyFkFGnJ9p","object":"chat.completion.chunk","created":1731749691,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_159d8341cc","choices":[],"usage":{"prompt_tokens":53,"completion_tokens":5,"total_tokens":58,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} + data: {"id":"chatcmpl-AXM5jAijInCrqEmlZs6ylqF3hpKe9","object":"chat.completion.chunk","created":1732513107,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_a7d06e42a7","choices":[],"usage":{"prompt_tokens":53,"completion_tokens":5,"total_tokens":58,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} data: [DONE] @@ -73,13 +75,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8e367cd11df226b0-SJC + - 8e7f4ae92cdafa62-SJC Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Sat, 16 Nov 2024 09:34:51 GMT + - Mon, 25 Nov 2024 05:38:27 GMT Server: - cloudflare Transfer-Encoding: @@ -91,7 +93,7 @@ interactions: alt-svc: - h3=":443"; ma=86400 openai-processing-ms: - - '238' + - '357' openai-version: - '2020-10-01' strict-transport-security: @@ -109,23 +111,24 @@ interactions: x-ratelimit-reset-tokens: - 44ms x-request-id: - - req_55c15dbef4c2f0a4026078c4d3051d04 + - req_ad4531da079aa84b23a02fcd6fcb5d45 status: code: 200 message: OK - request: - body: '{"messages": [{"role": "user", "content": "Return a country."}, {"role": - "assistant", "content": null, "tool_calls": [{"id": "call_wdEg17NjgwLbqVdKjq6MKLJ1", - "type": "function", "function": {"name": "return_country", "arguments": "{\"name\":\"Canada\"}"}}]}, - {"role": "tool", "tool_call_id": "call_wdEg17NjgwLbqVdKjq6MKLJ1", "content": - "1 validation error for Country\nname\n Value error, Country must be Ireland. - [type=value_error, input_value=''Canada'', input_type=str]\n For further - information visit https://errors.pydantic.dev/2.9/v/value_error"}], "model": - "gpt-4o", "parallel_tool_calls": false, "stream": true, "stream_options": {"include_usage": - true}, "tool_choice": {"type": "function", "function": {"name": "return_country"}}, - "tools": [{"type": "function", "function": {"name": "return_country", "parameters": - {"properties": {"name": {"title": "Name", "type": "string"}}, "required": ["name"], - "type": "object"}}}]}' + body: '{"messages": [{"role": "user", "content": "Return a country."}, {"content": + null, "refusal": null, "role": "assistant", "audio": null, "function_call": + null, "tool_calls": [{"id": "call_Cf3PFZFWlA0He0ZOgneuubd1", "function": {"arguments": + "{\"name\":\"country\"}", "name": "return_country", "parsed_arguments": null}, + "type": "function", "index": 0}], "parsed": null}, {"role": "tool", "tool_call_id": + "call_Cf3PFZFWlA0He0ZOgneuubd1", "content": "1 validation error for Country\nname\n Value + error, Country must be Ireland. [type=value_error, input_value=''country'', + input_type=str]\n For further information visit https://errors.pydantic.dev/2.9/v/value_error"}], + "model": "gpt-4o", "parallel_tool_calls": false, "stream": true, "stream_options": + {"include_usage": true}, "tool_choice": {"type": "function", "function": {"name": + "return_country"}}, "tools": [{"type": "function", "function": {"name": "return_country", + "parameters": {"properties": {"name": {"title": "Name", "type": "string"}}, + "required": ["name"], "type": "object"}}}]}' headers: accept: - application/json @@ -134,12 +137,9 @@ interactions: connection: - keep-alive content-length: - - '931' + - '1042' content-type: - application/json - cookie: - - __cf_bm=sPO4iMakhVSeN2qInDys83OtESskcGEQtUulfF25yU8-1731749691-1.0.1.1-bndGTvCeKjcqqi0MUQq4bEF3.atEdhQbF.8VGPbpjf1qoXcYhD5.v59eEOEpo6RpfegQYZGaUOPOT_fbY7Au1A; - _cfuvid=sCef7lr9GC3CdWBEwnTFFR7tRfAdsuJ0FQJjudsAOpQ-1731749691329-0.0.1.1-604800000 host: - api.openai.com user-agent: @@ -148,6 +148,8 @@ interactions: - arm64 x-stainless-async: - 'false' + x-stainless-helper-method: + - beta.chat.completions.stream x-stainless-lang: - python x-stainless-os: @@ -164,28 +166,28 @@ interactions: uri: https://api.openai.com/v1/chat/completions response: body: - string: 'data: {"id":"chatcmpl-AU9UZl5mf807mUw8xigORAVUueic3","object":"chat.completion.chunk","created":1731749691,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_bb84311112","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_cH1CgAuhZ0YHubyQGGfxl42Q","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} + string: 'data: {"id":"chatcmpl-AXM5k8UrAU68NVOIrhTyvKl5egZ6g","object":"chat.completion.chunk","created":1732513108,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_D3l38Wx0zyqPSyWngIhjMWVd","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UZl5mf807mUw8xigORAVUueic3","object":"chat.completion.chunk","created":1731749691,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_bb84311112","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5k8UrAU68NVOIrhTyvKl5egZ6g","object":"chat.completion.chunk","created":1732513108,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UZl5mf807mUw8xigORAVUueic3","object":"chat.completion.chunk","created":1731749691,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_bb84311112","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5k8UrAU68NVOIrhTyvKl5egZ6g","object":"chat.completion.chunk","created":1732513108,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UZl5mf807mUw8xigORAVUueic3","object":"chat.completion.chunk","created":1731749691,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_bb84311112","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5k8UrAU68NVOIrhTyvKl5egZ6g","object":"chat.completion.chunk","created":1732513108,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UZl5mf807mUw8xigORAVUueic3","object":"chat.completion.chunk","created":1731749691,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_bb84311112","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Ireland"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5k8UrAU68NVOIrhTyvKl5egZ6g","object":"chat.completion.chunk","created":1732513108,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Ireland"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UZl5mf807mUw8xigORAVUueic3","object":"chat.completion.chunk","created":1731749691,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_bb84311112","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5k8UrAU68NVOIrhTyvKl5egZ6g","object":"chat.completion.chunk","created":1732513108,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UZl5mf807mUw8xigORAVUueic3","object":"chat.completion.chunk","created":1731749691,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_bb84311112","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} + data: {"id":"chatcmpl-AXM5k8UrAU68NVOIrhTyvKl5egZ6g","object":"chat.completion.chunk","created":1732513108,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} - data: {"id":"chatcmpl-AU9UZl5mf807mUw8xigORAVUueic3","object":"chat.completion.chunk","created":1731749691,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_bb84311112","choices":[],"usage":{"prompt_tokens":125,"completion_tokens":5,"total_tokens":130,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} + data: {"id":"chatcmpl-AXM5k8UrAU68NVOIrhTyvKl5egZ6g","object":"chat.completion.chunk","created":1732513108,"model":"gpt-4o-2024-08-06","system_fingerprint":"fp_7f6be3efb0","choices":[],"usage":{"prompt_tokens":125,"completion_tokens":5,"total_tokens":130,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} data: [DONE] @@ -196,13 +198,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8e367cd34ff326b0-SJC + - 8e7f4aed6859fa62-SJC Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Sat, 16 Nov 2024 09:34:51 GMT + - Mon, 25 Nov 2024 05:38:28 GMT Server: - cloudflare Transfer-Encoding: @@ -214,7 +216,7 @@ interactions: alt-svc: - h3=":443"; ma=86400 openai-processing-ms: - - '356' + - '301' openai-version: - '2020-10-01' strict-transport-security: @@ -226,13 +228,13 @@ interactions: x-ratelimit-remaining-requests: - '499' x-ratelimit-remaining-tokens: - - '29913' + - '29923' x-ratelimit-reset-requests: - 120ms x-ratelimit-reset-tokens: - - 172ms + - 154ms x-request-id: - - req_d1d6dffe6036d31153d701cfd53dead1 + - req_611308496b6eaa46484a1c2ccfe4dfa7 status: code: 200 message: OK diff --git a/tests/chat_model/cassettes/test_retry_chat_model/test_retry_chat_model_acomplete_litellm_openai.yaml b/tests/chat_model/cassettes/test_retry_chat_model/test_retry_chat_model_acomplete_litellm_openai.yaml index 87b11758..60f2f570 100644 --- a/tests/chat_model/cassettes/test_retry_chat_model/test_retry_chat_model_acomplete_litellm_openai.yaml +++ b/tests/chat_model/cassettes/test_retry_chat_model/test_retry_chat_model_acomplete_litellm_openai.yaml @@ -33,7 +33,7 @@ interactions: x-stainless-raw-response: - 'true' x-stainless-retry-count: - - '1' + - '0' x-stainless-runtime: - CPython x-stainless-runtime-version: @@ -42,25 +42,25 @@ interactions: uri: https://api.openai.com/v1/chat/completions response: body: - string: 'data: {"id":"chatcmpl-AUTwhFJhWQohwNWkFZ4g3gdxmE7bs","object":"chat.completion.chunk","created":1731828315,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_ZsPgGThV4XYMbjLcJjngBbUb","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} + string: 'data: {"id":"chatcmpl-AY3jeRjlAKpYEV8HODjFYXYB67FlJ","object":"chat.completion.chunk","created":1732680874,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_i18HmT82n9PGc3mC6bAV7aXm","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwhFJhWQohwNWkFZ4g3gdxmE7bs","object":"chat.completion.chunk","created":1731828315,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-AY3jeRjlAKpYEV8HODjFYXYB67FlJ","object":"chat.completion.chunk","created":1732680874,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwhFJhWQohwNWkFZ4g3gdxmE7bs","object":"chat.completion.chunk","created":1731828315,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-AY3jeRjlAKpYEV8HODjFYXYB67FlJ","object":"chat.completion.chunk","created":1732680874,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwhFJhWQohwNWkFZ4g3gdxmE7bs","object":"chat.completion.chunk","created":1731828315,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-AY3jeRjlAKpYEV8HODjFYXYB67FlJ","object":"chat.completion.chunk","created":1732680874,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwhFJhWQohwNWkFZ4g3gdxmE7bs","object":"chat.completion.chunk","created":1731828315,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Canada"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-AY3jeRjlAKpYEV8HODjFYXYB67FlJ","object":"chat.completion.chunk","created":1732680874,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Canada"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwhFJhWQohwNWkFZ4g3gdxmE7bs","object":"chat.completion.chunk","created":1731828315,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-AY3jeRjlAKpYEV8HODjFYXYB67FlJ","object":"chat.completion.chunk","created":1732680874,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwhFJhWQohwNWkFZ4g3gdxmE7bs","object":"chat.completion.chunk","created":1731828315,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + data: {"id":"chatcmpl-AY3jeRjlAKpYEV8HODjFYXYB67FlJ","object":"chat.completion.chunk","created":1732680874,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} data: [DONE] @@ -71,13 +71,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8e3dfc5abc25645e-SJC + - 8e8f4acbbf48fa32-SJC Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Sun, 17 Nov 2024 07:25:15 GMT + - Wed, 27 Nov 2024 04:14:35 GMT Server: - cloudflare Transfer-Encoding: @@ -89,7 +89,7 @@ interactions: alt-svc: - h3=":443"; ma=86400 openai-processing-ms: - - '159' + - '118' openai-version: - '2020-10-01' strict-transport-security: @@ -103,26 +103,27 @@ interactions: x-ratelimit-remaining-tokens: - '199978' x-ratelimit-reset-requests: - - 24.784s + - 22.868s x-ratelimit-reset-tokens: - 6ms x-request-id: - - req_f8598a9c42d753bba944e8e4b8f405e6 + - req_efe987f6575e1d241fa34d5a2126225c status: code: 200 message: OK - request: - body: '{"messages": [{"role": "user", "content": "Return a country."}, {"role": - "assistant", "content": null, "tool_calls": [{"id": "call_ZsPgGThV4XYMbjLcJjngBbUb", - "type": "function", "function": {"name": "return_country", "arguments": "{\"name\":\"Canada\"}"}}]}, - {"role": "tool", "tool_call_id": "call_ZsPgGThV4XYMbjLcJjngBbUb", "content": - "1 validation error for Country\nname\n Value error, Country must be Ireland. - [type=value_error, input_value=''Canada'', input_type=str]\n For further - information visit https://errors.pydantic.dev/2.9/v/value_error"}], "model": - "gpt-4o-mini", "stream": true, "tool_choice": {"type": "function", "function": - {"name": "return_country"}}, "tools": [{"type": "function", "function": {"name": - "return_country", "parameters": {"properties": {"name": {"title": "Name", "type": - "string"}}, "required": ["name"], "type": "object"}}}]}' + body: '{"messages": [{"role": "user", "content": "Return a country."}, {"content": + null, "refusal": null, "role": "assistant", "audio": null, "function_call": + null, "tool_calls": [{"id": "call_i18HmT82n9PGc3mC6bAV7aXm", "function": {"arguments": + "{\"name\":\"Canada\"}", "name": "return_country", "parsed_arguments": null}, + "type": "function", "index": 0}], "parsed": null}, {"role": "tool", "tool_call_id": + "call_i18HmT82n9PGc3mC6bAV7aXm", "content": "1 validation error for Country\nname\n Value + error, Country must be Ireland. [type=value_error, input_value=''Canada'', input_type=str]\n For + further information visit https://errors.pydantic.dev/2.9/v/value_error"}], + "model": "gpt-4o-mini", "stream": true, "tool_choice": {"type": "function", + "function": {"name": "return_country"}}, "tools": [{"type": "function", "function": + {"name": "return_country", "parameters": {"properties": {"name": {"title": "Name", + "type": "string"}}, "required": ["name"], "type": "object"}}}]}' headers: accept: - application/json @@ -131,7 +132,7 @@ interactions: connection: - keep-alive content-length: - - '863' + - '972' content-type: - application/json host: @@ -160,25 +161,25 @@ interactions: uri: https://api.openai.com/v1/chat/completions response: body: - string: 'data: {"id":"chatcmpl-AUTwhAs71Rws8mFQuhs3PAeRXzEn4","object":"chat.completion.chunk","created":1731828315,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_EMNqE0XUFpOKbZaSHqCbbo9H","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} + string: 'data: {"id":"chatcmpl-AY3jfwOFPGQ40r0K3uGluzm6jKA9c","object":"chat.completion.chunk","created":1732680875,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_KK1SxLUkLnZAdf88EfFdFOoV","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwhAs71Rws8mFQuhs3PAeRXzEn4","object":"chat.completion.chunk","created":1731828315,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-AY3jfwOFPGQ40r0K3uGluzm6jKA9c","object":"chat.completion.chunk","created":1732680875,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwhAs71Rws8mFQuhs3PAeRXzEn4","object":"chat.completion.chunk","created":1731828315,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-AY3jfwOFPGQ40r0K3uGluzm6jKA9c","object":"chat.completion.chunk","created":1732680875,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwhAs71Rws8mFQuhs3PAeRXzEn4","object":"chat.completion.chunk","created":1731828315,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-AY3jfwOFPGQ40r0K3uGluzm6jKA9c","object":"chat.completion.chunk","created":1732680875,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwhAs71Rws8mFQuhs3PAeRXzEn4","object":"chat.completion.chunk","created":1731828315,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Ireland"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-AY3jfwOFPGQ40r0K3uGluzm6jKA9c","object":"chat.completion.chunk","created":1732680875,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Ireland"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwhAs71Rws8mFQuhs3PAeRXzEn4","object":"chat.completion.chunk","created":1731828315,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-AY3jfwOFPGQ40r0K3uGluzm6jKA9c","object":"chat.completion.chunk","created":1732680875,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwhAs71Rws8mFQuhs3PAeRXzEn4","object":"chat.completion.chunk","created":1731828315,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + data: {"id":"chatcmpl-AY3jfwOFPGQ40r0K3uGluzm6jKA9c","object":"chat.completion.chunk","created":1732680875,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} data: [DONE] @@ -189,13 +190,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8e3dfc5cbdaa645e-SJC + - 8e8f4acda93efa32-SJC Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Sun, 17 Nov 2024 07:25:15 GMT + - Wed, 27 Nov 2024 04:14:35 GMT Server: - cloudflare Transfer-Encoding: @@ -207,7 +208,7 @@ interactions: alt-svc: - h3=":443"; ma=86400 openai-processing-ms: - - '317' + - '315' openai-version: - '2020-10-01' strict-transport-security: @@ -221,11 +222,11 @@ interactions: x-ratelimit-remaining-tokens: - '199922' x-ratelimit-reset-requests: - - 33.112s + - 31.204s x-ratelimit-reset-tokens: - 23ms x-request-id: - - req_96c61728b21ad535d6bff2a9140a03a4 + - req_75ce8cf8ead16fbcebfa4f6ed9ca1ccf status: code: 200 message: OK diff --git a/tests/chat_model/cassettes/test_retry_chat_model/test_retry_chat_model_acomplete_openai.yaml b/tests/chat_model/cassettes/test_retry_chat_model/test_retry_chat_model_acomplete_openai.yaml index 2cb44e1e..302682eb 100644 --- a/tests/chat_model/cassettes/test_retry_chat_model/test_retry_chat_model_acomplete_openai.yaml +++ b/tests/chat_model/cassettes/test_retry_chat_model/test_retry_chat_model_acomplete_openai.yaml @@ -41,28 +41,28 @@ interactions: uri: https://api.openai.com/v1/chat/completions response: body: - string: 'data: {"id":"chatcmpl-AU9UMOiC2xzUTxC9coAuiEVSmvmZM","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_lecShqIgNwQziEZxdH7XFzD3","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} + string: 'data: {"id":"chatcmpl-AXM5iW6cFqElNxqivX4X88E8o7mfV","object":"chat.completion.chunk","created":1732513106,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_utLSDP4yL9s8N9dApjMxwrHA","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UMOiC2xzUTxC9coAuiEVSmvmZM","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5iW6cFqElNxqivX4X88E8o7mfV","object":"chat.completion.chunk","created":1732513106,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UMOiC2xzUTxC9coAuiEVSmvmZM","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5iW6cFqElNxqivX4X88E8o7mfV","object":"chat.completion.chunk","created":1732513106,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UMOiC2xzUTxC9coAuiEVSmvmZM","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5iW6cFqElNxqivX4X88E8o7mfV","object":"chat.completion.chunk","created":1732513106,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UMOiC2xzUTxC9coAuiEVSmvmZM","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Country"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5iW6cFqElNxqivX4X88E8o7mfV","object":"chat.completion.chunk","created":1732513106,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Canada"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UMOiC2xzUTxC9coAuiEVSmvmZM","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5iW6cFqElNxqivX4X88E8o7mfV","object":"chat.completion.chunk","created":1732513106,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UMOiC2xzUTxC9coAuiEVSmvmZM","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} + data: {"id":"chatcmpl-AXM5iW6cFqElNxqivX4X88E8o7mfV","object":"chat.completion.chunk","created":1732513106,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} - data: {"id":"chatcmpl-AU9UMOiC2xzUTxC9coAuiEVSmvmZM","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[],"usage":{"prompt_tokens":53,"completion_tokens":5,"total_tokens":58,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} + data: {"id":"chatcmpl-AXM5iW6cFqElNxqivX4X88E8o7mfV","object":"chat.completion.chunk","created":1732513106,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[],"usage":{"prompt_tokens":53,"completion_tokens":5,"total_tokens":58,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} data: [DONE] @@ -73,13 +73,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8e367c81cd98f94b-SJC + - 8e7f4ae52842968c-SJC Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Sat, 16 Nov 2024 09:34:38 GMT + - Mon, 25 Nov 2024 05:38:26 GMT Server: - cloudflare Transfer-Encoding: @@ -91,7 +91,7 @@ interactions: alt-svc: - h3=":443"; ma=86400 openai-processing-ms: - - '174' + - '132' openai-version: - '2020-10-01' strict-transport-security: @@ -105,23 +105,24 @@ interactions: x-ratelimit-remaining-tokens: - '199977' x-ratelimit-reset-requests: - - 25.227s + - 24.928s x-ratelimit-reset-tokens: - 6ms x-request-id: - - req_f5af46a001996c7a4c9db71df1c139eb + - req_5824fdf4a2e6d4b97abe71629d854c93 status: code: 200 message: OK - request: - body: '{"messages": [{"role": "user", "content": "Return a country."}, {"role": - "assistant", "content": null, "tool_calls": [{"id": "call_lecShqIgNwQziEZxdH7XFzD3", - "type": "function", "function": {"name": "return_country", "arguments": "{\"name\":\"Country\"}"}}]}, - {"role": "tool", "tool_call_id": "call_lecShqIgNwQziEZxdH7XFzD3", "content": - "1 validation error for Country\nname\n Value error, Country must be Ireland. - [type=value_error, input_value=''Country'', input_type=str]\n For further - information visit https://errors.pydantic.dev/2.9/v/value_error"}], "model": - "gpt-4o-mini", "parallel_tool_calls": false, "stream": true, "stream_options": + body: '{"messages": [{"role": "user", "content": "Return a country."}, {"content": + null, "refusal": null, "role": "assistant", "audio": null, "function_call": + null, "tool_calls": [{"id": "call_utLSDP4yL9s8N9dApjMxwrHA", "function": {"arguments": + "{\"name\":\"Canada\"}", "name": "return_country", "parsed_arguments": null}, + "type": "function", "index": 0}], "parsed": null}, {"role": "tool", "tool_call_id": + "call_utLSDP4yL9s8N9dApjMxwrHA", "content": "1 validation error for Country\nname\n Value + error, Country must be Ireland. [type=value_error, input_value=''Canada'', input_type=str]\n For + further information visit https://errors.pydantic.dev/2.9/v/value_error"}], + "model": "gpt-4o-mini", "parallel_tool_calls": false, "stream": true, "stream_options": {"include_usage": true}, "tool_choice": {"type": "function", "function": {"name": "return_country"}}, "tools": [{"type": "function", "function": {"name": "return_country", "parameters": {"properties": {"name": {"title": "Name", "type": "string"}}, @@ -134,12 +135,9 @@ interactions: connection: - keep-alive content-length: - - '938' + - '1045' content-type: - application/json - cookie: - - __cf_bm=uHzlI0jqu.pSi2pAubFTNeq6fPmJz5tfvnN5J3Womqk-1731749678-1.0.1.1-u5hCbyxg3fWVYgwkfVYOGMCgd7Cm2OlCCQL98Har0RraLlM_vn7gSoMmFUltlt9qfAzivRRupv6_o5u_OPY9DQ; - _cfuvid=Z2QTUH8.iKU7gXBDaD1tU2oRe6RBI5J31Rmexwn3jgk-1731749678578-0.0.1.1-604800000 host: - api.openai.com user-agent: @@ -164,28 +162,28 @@ interactions: uri: https://api.openai.com/v1/chat/completions response: body: - string: 'data: {"id":"chatcmpl-AU9UMp4Sg8yg8VBLhdhUOO2XJh0ay","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_MWI0aMZXWYCYRPgKpVjpxnZu","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} + string: 'data: {"id":"chatcmpl-AXM5jAXSOgYSkMYvw31lTlkWPjYBa","object":"chat.completion.chunk","created":1732513107,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_7tzq2Puv36M9dOdFVXqrJl4y","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UMp4Sg8yg8VBLhdhUOO2XJh0ay","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5jAXSOgYSkMYvw31lTlkWPjYBa","object":"chat.completion.chunk","created":1732513107,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UMp4Sg8yg8VBLhdhUOO2XJh0ay","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5jAXSOgYSkMYvw31lTlkWPjYBa","object":"chat.completion.chunk","created":1732513107,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UMp4Sg8yg8VBLhdhUOO2XJh0ay","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5jAXSOgYSkMYvw31lTlkWPjYBa","object":"chat.completion.chunk","created":1732513107,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UMp4Sg8yg8VBLhdhUOO2XJh0ay","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Ireland"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5jAXSOgYSkMYvw31lTlkWPjYBa","object":"chat.completion.chunk","created":1732513107,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Ireland"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UMp4Sg8yg8VBLhdhUOO2XJh0ay","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5jAXSOgYSkMYvw31lTlkWPjYBa","object":"chat.completion.chunk","created":1732513107,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UMp4Sg8yg8VBLhdhUOO2XJh0ay","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} + data: {"id":"chatcmpl-AXM5jAXSOgYSkMYvw31lTlkWPjYBa","object":"chat.completion.chunk","created":1732513107,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} - data: {"id":"chatcmpl-AU9UMp4Sg8yg8VBLhdhUOO2XJh0ay","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[],"usage":{"prompt_tokens":125,"completion_tokens":5,"total_tokens":130,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} + data: {"id":"chatcmpl-AXM5jAXSOgYSkMYvw31lTlkWPjYBa","object":"chat.completion.chunk","created":1732513107,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[],"usage":{"prompt_tokens":125,"completion_tokens":5,"total_tokens":130,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} data: [DONE] @@ -196,13 +194,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8e367c839ed0f94b-SJC + - 8e7f4ae6e980968c-SJC Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Sat, 16 Nov 2024 09:34:38 GMT + - Mon, 25 Nov 2024 05:38:27 GMT Server: - cloudflare Transfer-Encoding: @@ -214,7 +212,7 @@ interactions: alt-svc: - h3=":443"; ma=86400 openai-processing-ms: - - '133' + - '130' openai-version: - '2020-10-01' strict-transport-security: @@ -226,13 +224,13 @@ interactions: x-ratelimit-remaining-requests: - '9996' x-ratelimit-remaining-tokens: - - '199922' + - '199923' x-ratelimit-reset-requests: - - 33.576s + - 33.287s x-ratelimit-reset-tokens: - 23ms x-request-id: - - req_d2f9147adb2646dfd4f47a12389e0e1e + - req_a9ce5c6dc41085cadaa97544e11e2bb5 status: code: 200 message: OK diff --git a/tests/chat_model/cassettes/test_retry_chat_model/test_retry_chat_model_complete_litellm_openai.yaml b/tests/chat_model/cassettes/test_retry_chat_model/test_retry_chat_model_complete_litellm_openai.yaml index 2e0df828..d6ac20cb 100644 --- a/tests/chat_model/cassettes/test_retry_chat_model/test_retry_chat_model_complete_litellm_openai.yaml +++ b/tests/chat_model/cassettes/test_retry_chat_model/test_retry_chat_model_complete_litellm_openai.yaml @@ -42,25 +42,25 @@ interactions: uri: https://api.openai.com/v1/chat/completions response: body: - string: 'data: {"id":"chatcmpl-AUTwgzqd1Ii0FWSoryyv3Vasgy8Eh","object":"chat.completion.chunk","created":1731828314,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_pZHNlBWDbuQmYbidOEik4lYu","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} + string: 'data: {"id":"chatcmpl-AY3jbXL9GkFlYh7fPTgMPOTZpd9Q3","object":"chat.completion.chunk","created":1732680871,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_lB2W7ToZQx1KUeNj7US5se75","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwgzqd1Ii0FWSoryyv3Vasgy8Eh","object":"chat.completion.chunk","created":1731828314,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-AY3jbXL9GkFlYh7fPTgMPOTZpd9Q3","object":"chat.completion.chunk","created":1732680871,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwgzqd1Ii0FWSoryyv3Vasgy8Eh","object":"chat.completion.chunk","created":1731828314,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-AY3jbXL9GkFlYh7fPTgMPOTZpd9Q3","object":"chat.completion.chunk","created":1732680871,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwgzqd1Ii0FWSoryyv3Vasgy8Eh","object":"chat.completion.chunk","created":1731828314,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-AY3jbXL9GkFlYh7fPTgMPOTZpd9Q3","object":"chat.completion.chunk","created":1732680871,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwgzqd1Ii0FWSoryyv3Vasgy8Eh","object":"chat.completion.chunk","created":1731828314,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Country"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-AY3jbXL9GkFlYh7fPTgMPOTZpd9Q3","object":"chat.completion.chunk","created":1732680871,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Australia"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwgzqd1Ii0FWSoryyv3Vasgy8Eh","object":"chat.completion.chunk","created":1731828314,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-AY3jbXL9GkFlYh7fPTgMPOTZpd9Q3","object":"chat.completion.chunk","created":1732680871,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwgzqd1Ii0FWSoryyv3Vasgy8Eh","object":"chat.completion.chunk","created":1731828314,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + data: {"id":"chatcmpl-AY3jbXL9GkFlYh7fPTgMPOTZpd9Q3","object":"chat.completion.chunk","created":1732680871,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} data: [DONE] @@ -71,13 +71,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8e3dfc539ebfce84-SJC + - 8e8f4ab8aaae17de-SJC Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Sun, 17 Nov 2024 07:25:14 GMT + - Wed, 27 Nov 2024 04:14:32 GMT Server: - cloudflare Transfer-Encoding: @@ -89,7 +89,7 @@ interactions: alt-svc: - h3=":443"; ma=86400 openai-processing-ms: - - '139' + - '439' openai-version: - '2020-10-01' strict-transport-security: @@ -107,22 +107,23 @@ interactions: x-ratelimit-reset-tokens: - 6ms x-request-id: - - req_1c81b5404787e8c0c0fa39a40a5add39 + - req_24555d46defa5b03d2b639b26a1ec654 status: code: 200 message: OK - request: - body: '{"messages": [{"role": "user", "content": "Return a country."}, {"role": - "assistant", "content": null, "tool_calls": [{"id": "call_pZHNlBWDbuQmYbidOEik4lYu", - "type": "function", "function": {"name": "return_country", "arguments": "{\"name\":\"Country\"}"}}]}, - {"role": "tool", "tool_call_id": "call_pZHNlBWDbuQmYbidOEik4lYu", "content": - "1 validation error for Country\nname\n Value error, Country must be Ireland. - [type=value_error, input_value=''Country'', input_type=str]\n For further - information visit https://errors.pydantic.dev/2.9/v/value_error"}], "model": - "gpt-4o-mini", "stream": true, "tool_choice": {"type": "function", "function": - {"name": "return_country"}}, "tools": [{"type": "function", "function": {"name": - "return_country", "parameters": {"properties": {"name": {"title": "Name", "type": - "string"}}, "required": ["name"], "type": "object"}}}]}' + body: '{"messages": [{"role": "user", "content": "Return a country."}, {"content": + null, "refusal": null, "role": "assistant", "audio": null, "function_call": + null, "tool_calls": [{"id": "call_lB2W7ToZQx1KUeNj7US5se75", "function": {"arguments": + "{\"name\":\"Australia\"}", "name": "return_country", "parsed_arguments": null}, + "type": "function", "index": 0}], "parsed": null}, {"role": "tool", "tool_call_id": + "call_lB2W7ToZQx1KUeNj7US5se75", "content": "1 validation error for Country\nname\n Value + error, Country must be Ireland. [type=value_error, input_value=''Australia'', + input_type=str]\n For further information visit https://errors.pydantic.dev/2.9/v/value_error"}], + "model": "gpt-4o-mini", "stream": true, "tool_choice": {"type": "function", + "function": {"name": "return_country"}}, "tools": [{"type": "function", "function": + {"name": "return_country", "parameters": {"properties": {"name": {"title": "Name", + "type": "string"}}, "required": ["name"], "type": "object"}}}]}' headers: accept: - application/json @@ -131,7 +132,7 @@ interactions: connection: - keep-alive content-length: - - '865' + - '978' content-type: - application/json host: @@ -160,25 +161,25 @@ interactions: uri: https://api.openai.com/v1/chat/completions response: body: - string: 'data: {"id":"chatcmpl-AUTwgV2Ef5xeCpenezTaQr3IFb6WY","object":"chat.completion.chunk","created":1731828314,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_L9h05hjrL8hqC935gkuWhRXN","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} + string: 'data: {"id":"chatcmpl-AY3jdjAv7PKYqsZYyBXB2QntEWtTt","object":"chat.completion.chunk","created":1732680873,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_GIpaN9ZLbC2qxUsQYARPQRDO","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwgV2Ef5xeCpenezTaQr3IFb6WY","object":"chat.completion.chunk","created":1731828314,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-AY3jdjAv7PKYqsZYyBXB2QntEWtTt","object":"chat.completion.chunk","created":1732680873,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwgV2Ef5xeCpenezTaQr3IFb6WY","object":"chat.completion.chunk","created":1731828314,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-AY3jdjAv7PKYqsZYyBXB2QntEWtTt","object":"chat.completion.chunk","created":1732680873,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwgV2Ef5xeCpenezTaQr3IFb6WY","object":"chat.completion.chunk","created":1731828314,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-AY3jdjAv7PKYqsZYyBXB2QntEWtTt","object":"chat.completion.chunk","created":1732680873,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwgV2Ef5xeCpenezTaQr3IFb6WY","object":"chat.completion.chunk","created":1731828314,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Ireland"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-AY3jdjAv7PKYqsZYyBXB2QntEWtTt","object":"chat.completion.chunk","created":1732680873,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Ireland"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwgV2Ef5xeCpenezTaQr3IFb6WY","object":"chat.completion.chunk","created":1731828314,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} + data: {"id":"chatcmpl-AY3jdjAv7PKYqsZYyBXB2QntEWtTt","object":"chat.completion.chunk","created":1732680873,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} - data: {"id":"chatcmpl-AUTwgV2Ef5xeCpenezTaQr3IFb6WY","object":"chat.completion.chunk","created":1731828314,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} + data: {"id":"chatcmpl-AY3jdjAv7PKYqsZYyBXB2QntEWtTt","object":"chat.completion.chunk","created":1732680873,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} data: [DONE] @@ -186,14 +187,16 @@ interactions: ' headers: + CF-Cache-Status: + - DYNAMIC CF-RAY: - - 8e3dfc556828ce84-SJC + - 8e8f4abf2a8c17de-SJC Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Sun, 17 Nov 2024 07:25:14 GMT + - Wed, 27 Nov 2024 04:14:34 GMT Server: - cloudflare Transfer-Encoding: @@ -204,10 +207,8 @@ interactions: - X-Request-ID alt-svc: - h3=":443"; ma=86400 - cf-cache-status: - - DYNAMIC openai-processing-ms: - - '130' + - '1524' openai-version: - '2020-10-01' strict-transport-security: @@ -221,11 +222,11 @@ interactions: x-ratelimit-remaining-tokens: - '199922' x-ratelimit-reset-requests: - - 16.993s + - 16.281s x-ratelimit-reset-tokens: - 23ms x-request-id: - - req_cb0ee756dfff6d657b46133aae317271 + - req_c104cb3bc8f86806512bc2c26f592d68 status: code: 200 message: OK diff --git a/tests/chat_model/cassettes/test_retry_chat_model/test_retry_chat_model_complete_openai.yaml b/tests/chat_model/cassettes/test_retry_chat_model/test_retry_chat_model_complete_openai.yaml index eca411bf..6d2e7782 100644 --- a/tests/chat_model/cassettes/test_retry_chat_model/test_retry_chat_model_complete_openai.yaml +++ b/tests/chat_model/cassettes/test_retry_chat_model/test_retry_chat_model_complete_openai.yaml @@ -25,6 +25,8 @@ interactions: - arm64 x-stainless-async: - 'false' + x-stainless-helper-method: + - beta.chat.completions.stream x-stainless-lang: - python x-stainless-os: @@ -41,28 +43,28 @@ interactions: uri: https://api.openai.com/v1/chat/completions response: body: - string: 'data: {"id":"chatcmpl-AU9ULww3MRjPquqNLMfEm0wRtvXCp","object":"chat.completion.chunk","created":1731749677,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_ugvIEdaAkA3EwKchrQL6sCtt","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} + string: 'data: {"id":"chatcmpl-AXM5h5kzhX8FtXccw9UdtcvBLAS6Z","object":"chat.completion.chunk","created":1732513105,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_2AGPVxgioaHgJ0ysmjvWE4a9","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9ULww3MRjPquqNLMfEm0wRtvXCp","object":"chat.completion.chunk","created":1731749677,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5h5kzhX8FtXccw9UdtcvBLAS6Z","object":"chat.completion.chunk","created":1732513105,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9ULww3MRjPquqNLMfEm0wRtvXCp","object":"chat.completion.chunk","created":1731749677,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5h5kzhX8FtXccw9UdtcvBLAS6Z","object":"chat.completion.chunk","created":1732513105,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9ULww3MRjPquqNLMfEm0wRtvXCp","object":"chat.completion.chunk","created":1731749677,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5h5kzhX8FtXccw9UdtcvBLAS6Z","object":"chat.completion.chunk","created":1732513105,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9ULww3MRjPquqNLMfEm0wRtvXCp","object":"chat.completion.chunk","created":1731749677,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Canada"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5h5kzhX8FtXccw9UdtcvBLAS6Z","object":"chat.completion.chunk","created":1732513105,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Canada"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9ULww3MRjPquqNLMfEm0wRtvXCp","object":"chat.completion.chunk","created":1731749677,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5h5kzhX8FtXccw9UdtcvBLAS6Z","object":"chat.completion.chunk","created":1732513105,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9ULww3MRjPquqNLMfEm0wRtvXCp","object":"chat.completion.chunk","created":1731749677,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} + data: {"id":"chatcmpl-AXM5h5kzhX8FtXccw9UdtcvBLAS6Z","object":"chat.completion.chunk","created":1732513105,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} - data: {"id":"chatcmpl-AU9ULww3MRjPquqNLMfEm0wRtvXCp","object":"chat.completion.chunk","created":1731749677,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0ba0d124f1","choices":[],"usage":{"prompt_tokens":53,"completion_tokens":5,"total_tokens":58,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} + data: {"id":"chatcmpl-AXM5h5kzhX8FtXccw9UdtcvBLAS6Z","object":"chat.completion.chunk","created":1732513105,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[],"usage":{"prompt_tokens":53,"completion_tokens":5,"total_tokens":58,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} data: [DONE] @@ -73,13 +75,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8e367c7d7c6fcf9f-SJC + - 8e7f4adefa511742-SJC Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Sat, 16 Nov 2024 09:34:37 GMT + - Mon, 25 Nov 2024 05:38:25 GMT Server: - cloudflare Transfer-Encoding: @@ -91,7 +93,7 @@ interactions: alt-svc: - h3=":443"; ma=86400 openai-processing-ms: - - '161' + - '142' openai-version: - '2020-10-01' strict-transport-security: @@ -103,25 +105,26 @@ interactions: x-ratelimit-remaining-requests: - '9999' x-ratelimit-remaining-tokens: - - '199978' + - '199977' x-ratelimit-reset-requests: - 8.64s x-ratelimit-reset-tokens: - 6ms x-request-id: - - req_5b1cdde6bb1c37d9615f2a7f6322264f + - req_39f5a155d1bb5b52ff4716ed53c116bb status: code: 200 message: OK - request: - body: '{"messages": [{"role": "user", "content": "Return a country."}, {"role": - "assistant", "content": null, "tool_calls": [{"id": "call_ugvIEdaAkA3EwKchrQL6sCtt", - "type": "function", "function": {"name": "return_country", "arguments": "{\"name\":\"Canada\"}"}}]}, - {"role": "tool", "tool_call_id": "call_ugvIEdaAkA3EwKchrQL6sCtt", "content": - "1 validation error for Country\nname\n Value error, Country must be Ireland. - [type=value_error, input_value=''Canada'', input_type=str]\n For further - information visit https://errors.pydantic.dev/2.9/v/value_error"}], "model": - "gpt-4o-mini", "parallel_tool_calls": false, "stream": true, "stream_options": + body: '{"messages": [{"role": "user", "content": "Return a country."}, {"content": + null, "refusal": null, "role": "assistant", "audio": null, "function_call": + null, "tool_calls": [{"id": "call_2AGPVxgioaHgJ0ysmjvWE4a9", "function": {"arguments": + "{\"name\":\"Canada\"}", "name": "return_country", "parsed_arguments": null}, + "type": "function", "index": 0}], "parsed": null}, {"role": "tool", "tool_call_id": + "call_2AGPVxgioaHgJ0ysmjvWE4a9", "content": "1 validation error for Country\nname\n Value + error, Country must be Ireland. [type=value_error, input_value=''Canada'', input_type=str]\n For + further information visit https://errors.pydantic.dev/2.9/v/value_error"}], + "model": "gpt-4o-mini", "parallel_tool_calls": false, "stream": true, "stream_options": {"include_usage": true}, "tool_choice": {"type": "function", "function": {"name": "return_country"}}, "tools": [{"type": "function", "function": {"name": "return_country", "parameters": {"properties": {"name": {"title": "Name", "type": "string"}}, @@ -134,12 +137,9 @@ interactions: connection: - keep-alive content-length: - - '936' + - '1045' content-type: - application/json - cookie: - - __cf_bm=IongOs.UaKxgqpxpk2cJio1t2ANoC2tmOxc3A6LF69Q-1731749677-1.0.1.1-l.m7XlTBhSnwT2YxmLrxCxFdERRQVe2bjNJTKpj.cnlBWlYXqfutVA7.lfFBsRZ5hIkqC92SBz2BmluvkOYDEw; - _cfuvid=xilQfC5pUNDO3wH0u9UbngFg3782S5FE7fn8T37Dhds-1731749677873-0.0.1.1-604800000 host: - api.openai.com user-agent: @@ -148,6 +148,8 @@ interactions: - arm64 x-stainless-async: - 'false' + x-stainless-helper-method: + - beta.chat.completions.stream x-stainless-lang: - python x-stainless-os: @@ -164,28 +166,28 @@ interactions: uri: https://api.openai.com/v1/chat/completions response: body: - string: 'data: {"id":"chatcmpl-AU9UMCyEsQwtrRJ1NQD4DQkDoIDTn","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_Eyrvzk11kYJM0px7uwZFVr7m","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} + string: 'data: {"id":"chatcmpl-AXM5iy0XofVHS1yfehrUoO6LZUuJ6","object":"chat.completion.chunk","created":1732513106,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_1BMcenaJshxLU4S6ndqKbYrL","type":"function","function":{"name":"return_country","arguments":""}}],"refusal":null},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UMCyEsQwtrRJ1NQD4DQkDoIDTn","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5iy0XofVHS1yfehrUoO6LZUuJ6","object":"chat.completion.chunk","created":1732513106,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UMCyEsQwtrRJ1NQD4DQkDoIDTn","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5iy0XofVHS1yfehrUoO6LZUuJ6","object":"chat.completion.chunk","created":1732513106,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"name"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UMCyEsQwtrRJ1NQD4DQkDoIDTn","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5iy0XofVHS1yfehrUoO6LZUuJ6","object":"chat.completion.chunk","created":1732513106,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UMCyEsQwtrRJ1NQD4DQkDoIDTn","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Ireland"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5iy0XofVHS1yfehrUoO6LZUuJ6","object":"chat.completion.chunk","created":1732513106,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Ireland"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UMCyEsQwtrRJ1NQD4DQkDoIDTn","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} + data: {"id":"chatcmpl-AXM5iy0XofVHS1yfehrUoO6LZUuJ6","object":"chat.completion.chunk","created":1732513106,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null} - data: {"id":"chatcmpl-AU9UMCyEsQwtrRJ1NQD4DQkDoIDTn","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} + data: {"id":"chatcmpl-AXM5iy0XofVHS1yfehrUoO6LZUuJ6","object":"chat.completion.chunk","created":1732513106,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null} - data: {"id":"chatcmpl-AU9UMCyEsQwtrRJ1NQD4DQkDoIDTn","object":"chat.completion.chunk","created":1731749678,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_9b78b61c52","choices":[],"usage":{"prompt_tokens":125,"completion_tokens":5,"total_tokens":130,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} + data: {"id":"chatcmpl-AXM5iy0XofVHS1yfehrUoO6LZUuJ6","object":"chat.completion.chunk","created":1732513106,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[],"usage":{"prompt_tokens":125,"completion_tokens":5,"total_tokens":130,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}} data: [DONE] @@ -196,13 +198,13 @@ interactions: CF-Cache-Status: - DYNAMIC CF-RAY: - - 8e367c7f3e0dcf9f-SJC + - 8e7f4ae18d3c1742-SJC Connection: - keep-alive Content-Type: - text/event-stream; charset=utf-8 Date: - - Sat, 16 Nov 2024 09:34:38 GMT + - Mon, 25 Nov 2024 05:38:26 GMT Server: - cloudflare Transfer-Encoding: @@ -214,7 +216,7 @@ interactions: alt-svc: - h3=":443"; ma=86400 openai-processing-ms: - - '255' + - '220' openai-version: - '2020-10-01' strict-transport-security: @@ -226,13 +228,13 @@ interactions: x-ratelimit-remaining-requests: - '9998' x-ratelimit-remaining-tokens: - - '199922' + - '199923' x-ratelimit-reset-requests: - - 17.001s + - 16.863s x-ratelimit-reset-tokens: - 23ms x-request-id: - - req_2740ae04afa64140db7a7b0f3dd2516e + - req_d6456abe3299a66d7b7a4060c342d7d1 status: code: 200 message: OK