From 8f128e0c1029c7b41e5ac28deaef954ba45755fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=A1vio=20Juvenal?= Date: Fri, 4 Oct 2024 16:29:42 -0300 Subject: [PATCH 1/2] Store all messages (include tool calls) --- django_ai_assistant/helpers/assistants.py | 50 +- .../helpers/django_messages.py | 49 ++ django_ai_assistant/helpers/use_cases.py | 7 +- .../langchain/chat_message_histories.py | 175 ---- django_ai_assistant/models.py | 43 +- example/weather/ai_assistants.py | 2 +- tests/langchain/__init__.py | 0 .../langchain/test_chat_message_histories.py | 246 ------ .../test_AIAssistant_invoke.yaml | 821 ++---------------- .../test_AIAssistant_with_rag_invoke.yaml | 522 +---------- tests/test_helpers/test_assistants.py | 115 ++- tests/test_views.py | 21 +- 12 files changed, 314 insertions(+), 1737 deletions(-) create mode 100644 django_ai_assistant/helpers/django_messages.py delete mode 100644 django_ai_assistant/langchain/chat_message_histories.py delete mode 100644 tests/langchain/__init__.py delete mode 100644 tests/langchain/test_chat_message_histories.py diff --git a/django_ai_assistant/helpers/assistants.py b/django_ai_assistant/helpers/assistants.py index 39598cb..2813473 100644 --- a/django_ai_assistant/helpers/assistants.py +++ b/django_ai_assistant/helpers/assistants.py @@ -13,7 +13,6 @@ AIMessage, AnyMessage, BaseMessage, - ChatMessage, HumanMessage, SystemMessage, ) @@ -34,8 +33,7 @@ ) from langchain_core.tools import BaseTool from langchain_openai import ChatOpenAI -from langgraph.graph import END, StateGraph -from langgraph.graph.message import add_messages +from langgraph.graph import END, StateGraph, add_messages from langgraph.prebuilt import ToolNode from pydantic import BaseModel @@ -43,6 +41,7 @@ from django_ai_assistant.exceptions import ( AIAssistantMisconfiguredError, ) +from django_ai_assistant.helpers.django_messages import save_django_messages from django_ai_assistant.langchain.tools import tool as tool_decorator @@ -417,37 +416,27 @@ def as_graph(self, thread_id: Any | None = None) -> Runnable[dict, dict]: Returns: the compiled graph """ - # DjangoChatMessageHistory must be here because Django may not be loaded yet elsewhere. - # DjangoChatMessageHistory was used in the context of langchain, now that we are using - # langgraph this can be further simplified by just porting the add_messages logic. - from django_ai_assistant.langchain.chat_message_histories import ( - DjangoChatMessageHistory, - ) - - message_history = DjangoChatMessageHistory(thread_id) if thread_id else None + from django_ai_assistant.models import Thread llm = self.get_llm() tools = self.get_tools() llm_with_tools = llm.bind_tools(tools) if tools else llm + if thread_id: + thread = Thread.objects.get(id=thread_id) + else: + thread = None def custom_add_messages(left: list[BaseMessage], right: list[BaseMessage]): result = add_messages(left, right) # type: ignore - - if message_history: - messages_to_store = [ - m - for m in result - if isinstance(m, HumanMessage | ChatMessage) - or (isinstance(m, AIMessage) and not m.tool_calls) - ] - message_history.add_messages(messages_to_store) - + if thread: + # Save all messages, except the initial system message: + thread_messages = [m for m in result if not isinstance(m, SystemMessage)] + save_django_messages(cast(list[BaseMessage], thread_messages), thread=thread) return result class AgentState(TypedDict): messages: Annotated[list[AnyMessage], custom_add_messages] - input: str # noqa: A003 - context: str + input: str | None # noqa: A003 output: Any def setup(state: AgentState): @@ -455,8 +444,11 @@ def setup(state: AgentState): return {"messages": [SystemMessage(content=system_prompt)]} def history(state: AgentState): - history = message_history.messages if message_history else [] - return {"messages": [*history, HumanMessage(content=state["input"])]} + messages = thread.get_messages(include_extra_messages=True) if thread else [] + if state["input"]: + messages.append(HumanMessage(content=state["input"])) + + return {"messages": messages} def retriever(state: AgentState): if not self.has_rag: @@ -465,8 +457,9 @@ def retriever(state: AgentState): retriever = self.get_history_aware_retriever() # Remove the initial instructions to prevent having two SystemMessages # This is necessary for compatibility with Anthropic - messages_without_input = state["messages"][1:-1] - docs = retriever.invoke({"input": state["input"], "history": messages_without_input}) + messages_to_summarize = state["messages"][1:-1] + input_message = state["messages"][-1] + docs = retriever.invoke({"input": input_message, "history": messages_to_summarize}) document_separator = self.get_document_separator() document_prompt = self.get_document_prompt() @@ -550,7 +543,8 @@ def invoke(self, *args: Any, thread_id: Any | None, **kwargs: Any) -> dict: Args: *args: Positional arguments to pass to the graph. - Make sure to include a `dict` like `{"input": "user message"}`. + To add a new message, use a dict like `{"input": "user message"}`. + If thread already has a `HumanMessage` in the end, you can invoke without args. thread_id (Any | None): The thread ID for the chat message history. If `None`, an in-memory chat message history is used. **kwargs: Keyword arguments to pass to the graph. diff --git a/django_ai_assistant/helpers/django_messages.py b/django_ai_assistant/helpers/django_messages.py new file mode 100644 index 0000000..4ed501c --- /dev/null +++ b/django_ai_assistant/helpers/django_messages.py @@ -0,0 +1,49 @@ +from typing import TYPE_CHECKING + +from django.db import transaction + +from langchain_core.messages import ( + BaseMessage, + message_to_dict, +) + + +if TYPE_CHECKING: + from django_ai_assistant.models import Message as DjangoMessage + from django_ai_assistant.models import Thread + + +@transaction.atomic +def save_django_messages(messages: list[BaseMessage], thread: "Thread") -> list["DjangoMessage"]: + """ + Save a list of messages to the Django database. + Note: Changes the message objects in place by changing each message.id to the Django ID. + + Args: + messages (list[BaseMessage]): The list of messages to save. + thread (Thread): The thread to save the messages to. + """ + + from django_ai_assistant.models import Message as DjangoMessage + + existing_message_ids = [ + str(i) + for i in DjangoMessage.objects.filter(thread=thread) + .order_by("created_at") + .values_list("id", flat=True) + ] + + messages_to_create = [m for m in messages if m.id not in existing_message_ids] + + created_messages = DjangoMessage.objects.bulk_create( + [DjangoMessage(thread=thread, message={}) for _ in messages_to_create] + ) + + # Update langchain message IDs with Django message IDs + for idx, created_message in enumerate(created_messages): + message_with_id = messages_to_create[idx] + message_with_id.id = str(created_message.id) + created_message.message = message_to_dict(message_with_id) + + DjangoMessage.objects.bulk_update(created_messages, ["message"]) + return created_messages diff --git a/django_ai_assistant/helpers/use_cases.py b/django_ai_assistant/helpers/use_cases.py index a2c46a7..9ea061c 100644 --- a/django_ai_assistant/helpers/use_cases.py +++ b/django_ai_assistant/helpers/use_cases.py @@ -9,7 +9,6 @@ AIUserNotAllowedError, ) from django_ai_assistant.helpers.assistants import AIAssistant -from django_ai_assistant.langchain.chat_message_histories import DjangoChatMessageHistory from django_ai_assistant.models import Message, Thread from django_ai_assistant.permissions import ( can_create_message, @@ -291,7 +290,7 @@ def get_thread_messages( if user != thread.created_by: raise AIUserNotAllowedError("User is not allowed to view messages in this thread") - return DjangoChatMessageHistory(thread.id).get_messages() + return thread.get_messages(include_extra_messages=False) def delete_message( @@ -312,6 +311,4 @@ def delete_message( if not can_delete_message(message=message, user=user, request=request): raise AIUserNotAllowedError("User is not allowed to delete this message") - return DjangoChatMessageHistory(thread_id=message.thread_id).remove_messages( - message_ids=[str(message.id)] - ) + return message.delete() diff --git a/django_ai_assistant/langchain/chat_message_histories.py b/django_ai_assistant/langchain/chat_message_histories.py deleted file mode 100644 index 8291cdf..0000000 --- a/django_ai_assistant/langchain/chat_message_histories.py +++ /dev/null @@ -1,175 +0,0 @@ -"""Client for persisting chat message history in a Django database. -This client provides support for both sync and async. -Note: the Django database must support JSONField. -See: https://docs.djangoproject.com/en/dev/ref/models/fields/#jsonfield -Based on langchain-postgres -""" -from __future__ import annotations - -import logging -from typing import Any, List, Sequence, cast - -from django.db import transaction - -from langchain_core.chat_history import BaseChatMessageHistory -from langchain_core.messages import ( - BaseMessage, - message_to_dict, - messages_from_dict, -) - -from django_ai_assistant.decorators import with_cast_id -from django_ai_assistant.models import Message - - -logger = logging.getLogger(__name__) - - -class DjangoChatMessageHistory(BaseChatMessageHistory): - @with_cast_id - def __init__( - self, - thread_id: Any, - ) -> None: - """Client for persisting chat message history in a Django database. - - This client provides support for both sync and async. - - The client provides methods to add messages, get messages, - and clear the chat message history of a thread. - - The schema has the following columns: - - - id: A serial primary key. - - thread_id: The thread ID for the chat message history. - - message: The JSONB message content. - - created_at: The timestamp of when the message was created. - - Messages are retrieved for a given thread_id and are sorted by - the order in which the messages were added to the thread. - - The "created_at" column is not returned by the interface, but - has been added for the schema so the information is available in the database. - - A thread_id can be used to separate different chat histories in the same table, - the thread_id should be provided when initializing the client. - - This client needs to be used in Django context to interact with the database. - - Args: - thread_id: The thread ID to use for the chat message history - sync_connection: An existing psycopg connection instance - async_connection: An existing psycopg async connection instance - - Usage: - - Initialize the class with the appropriate thread ID, table name, - and database connection. - - Add messages to the database using add_messages or aadd_messages. - - Retrieve messages with get_messages or aget_messages. - - Clear the thread with clear or aclear when needed. - """ - self._thread_id = thread_id - - def add_messages(self, messages: Sequence[BaseMessage]) -> None: - """Add messages to the chat thread. - - Args: - messages: A list of BaseMessage objects to store. - """ - with transaction.atomic(): - existing_message_ids = [ - str(i) for i in self._get_messages_qs().values_list("id", flat=True) - ] - - messages_to_create = [m for m in messages if m.id not in existing_message_ids] - - created_messages = Message.objects.bulk_create( - [Message(thread_id=self._thread_id, message=dict()) for _ in messages_to_create] - ) - - # Update langchain message IDs with Django message IDs - for idx, created_message in enumerate(created_messages): - message_with_id = messages_to_create[idx] - message_with_id.id = str(created_message.id) - created_message.message = message_to_dict(message_with_id) - - Message.objects.bulk_update(created_messages, ["message"]) - - async def aadd_messages(self, messages: Sequence[BaseMessage]) -> None: - """Add messages to the chat thread. - - Args: - messages: A list of BaseMessage objects to store. - """ - existing_message_ids = [ - str(i) async for i in self._get_messages_qs().values_list("id", flat=True) - ] - - messages_to_create = [m for m in messages if m.id not in existing_message_ids] - - # NOTE: This method does not use transactions because it do not yet work in async mode. - # Source: https://docs.djangoproject.com/en/5.0/topics/async/#queries-the-orm - created_messages = await Message.objects.abulk_create( - [Message(thread_id=self._thread_id, message=dict()) for _ in messages_to_create] - ) - - # Update langchain message IDs with Django message IDs - for idx, created_message in enumerate(created_messages): - message_with_id = messages_to_create[idx] - message_with_id.id = str(created_message.id) - created_message.message = message_to_dict(message_with_id) - - await Message.objects.abulk_update(created_messages, ["message"]) - - @with_cast_id - def remove_messages(self, message_ids: List[Any]) -> None: - """Remove messages from the chat thread. - - Args: - message_ids: A list of message IDs to remove. - """ - Message.objects.filter(id__in=message_ids).delete() - - @with_cast_id - async def aremove_messages(self, message_ids: List[Any]) -> None: - """Remove messages from the chat thread. - - Args: - message_ids: A list of message IDs to remove. - """ - await Message.objects.filter(id__in=message_ids).adelete() - - def _get_messages_qs(self): - return Message.objects.filter(thread_id=self._thread_id).order_by("created_at") - - def get_messages(self) -> List[BaseMessage]: - """Retrieve messages from the chat thread.""" - items = cast( - Sequence[dict], - self._get_messages_qs().values_list("message", flat=True), - ) - messages = messages_from_dict(items) - return messages - - async def aget_messages(self) -> List[BaseMessage]: - """Retrieve messages from the thread.""" - items = [] - async for m in self._get_messages_qs().values_list("message", flat=True): - items.append(m) - - items = cast(Sequence[dict], items) - messages = messages_from_dict(items) - return messages - - @property - def messages(self) -> List[BaseMessage]: - """Retrieve messages from the thread.""" - return self.get_messages() - - def clear(self) -> None: - """Clear the chat message history for the thread.""" - Message.objects.filter(thread_id=self._thread_id).delete() - - async def aclear(self) -> None: - """Clear the chat message history for the GIVEN thread.""" - await Message.objects.filter(thread_id=self._thread_id).adelete() diff --git a/django_ai_assistant/models.py b/django_ai_assistant/models.py index b7360ab..caa5b4d 100644 --- a/django_ai_assistant/models.py +++ b/django_ai_assistant/models.py @@ -1,15 +1,24 @@ import json -from typing import Any +from typing import Any, Sequence, cast from django.conf import settings from django.db import models from django.db.models import F, Index, Manager +from langchain_core.messages import ( + AIMessage, + BaseMessage, + ChatMessage, + HumanMessage, + messages_from_dict, +) + class Thread(models.Model): """Thread model. A thread is a collection of messages between a user and the AI assistant. Also called conversation or session.""" + id: Any # noqa: A003 messages: Manager["Message"] name = models.CharField(max_length=255, blank=True) """Name of the thread. Can be blank.""" @@ -43,22 +52,50 @@ def __repr__(self) -> str: """Return the string representation of the thread like ''""" return f"" + def get_messages(self, include_extra_messages: bool = False) -> list[BaseMessage]: + """ + Get Langchain messages objects from the thread. + + Args: + include_extra_messages (bool): Whether to include non-chat messages (like tool calls). + + Returns: + list[BaseMessage]: List of messages + """ + + messages = messages_from_dict( + cast( + Sequence[dict[str, BaseMessage]], + Message.objects.filter(thread=self) + .order_by("created_at") + .values_list("message", flat=True), + ) + ) + if not include_extra_messages: + messages = [ + m + for m in messages + if isinstance(m, HumanMessage | ChatMessage) + or (isinstance(m, AIMessage) and not m.tool_calls) + ] + return cast(list[BaseMessage], messages) + class Message(models.Model): """Message model. A message is a text that is part of a thread. A message can be sent by a user or the AI assistant.\n The message data is stored as a JSON field called `message`.""" + id: Any # noqa: A003 thread = models.ForeignKey(Thread, on_delete=models.CASCADE, related_name="messages") """Thread to which the message belongs.""" - thread_id: Any # noqa: A003 + thread_id: Any message = models.JSONField() """Message content. This is a serialized Langchain `BaseMessage` that was serialized with `message_to_dict` and can be deserialized with `messages_from_dict`.""" created_at = models.DateTimeField(auto_now_add=True) """Date and time when the message was created. Automatically set when the message is created.""" - # TODO: add created_by field class Meta: verbose_name = "Message" diff --git a/example/weather/ai_assistants.py b/example/weather/ai_assistants.py index a405cbf..7f58e43 100644 --- a/example/weather/ai_assistants.py +++ b/example/weather/ai_assistants.py @@ -59,7 +59,7 @@ def fetch_forecast_weather(self, location, forecast_date) -> dict: return response.json() @method_tool - def who_am_i(self) -> str: + def get_username(self) -> str: """Return the username of the current user""" if self._user: return self._user.username diff --git a/tests/langchain/__init__.py b/tests/langchain/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/langchain/test_chat_message_histories.py b/tests/langchain/test_chat_message_histories.py deleted file mode 100644 index 4e54cce..0000000 --- a/tests/langchain/test_chat_message_histories.py +++ /dev/null @@ -1,246 +0,0 @@ -import pytest -from langchain_core.messages import AIMessage, HumanMessage - -from django_ai_assistant.langchain.chat_message_histories import DjangoChatMessageHistory -from django_ai_assistant.models import Message, Thread - - -@pytest.fixture() -def thread_aaa(db): - return Thread.objects.create(name="AAA") - - -@pytest.fixture() -def thread_bbb(db): - return Thread.objects.create(name="BBB") - - -def test_add_messages(thread_aaa, thread_bbb): - thread = Thread.objects.get(name="AAA") - other_thread = Thread.objects.get(name="BBB") - - message_history = DjangoChatMessageHistory(thread_id=thread.id) - message_history.add_messages( - [HumanMessage(content="Hello, world!"), AIMessage(content="Hi! How are you?")] - ) - - assert thread.messages.count() == 2 - message_0 = thread.messages.order_by("created_at").first() - assert message_0.message["data"]["content"] == "Hello, world!" - assert message_0.message["type"] == "human" - message_1 = thread.messages.order_by("created_at").last() - assert message_1.message["data"]["content"] == "Hi! How are you?" - assert message_1.message["type"] == "ai" - assert other_thread.messages.count() == 0 - - -@pytest.mark.asyncio -@pytest.mark.django_db(transaction=True) -async def test_aadd_messages(thread_aaa, thread_bbb): - thread = await Thread.objects.aget(name="AAA") - other_thread = await Thread.objects.aget(name="BBB") - - message_history = DjangoChatMessageHistory(thread_id=thread.id) - await message_history.aadd_messages( - [HumanMessage(content="Hello, world!"), AIMessage(content="Hi! How are you?")] - ) - - assert await thread.messages.acount() == 2 - message_0 = await thread.messages.order_by("created_at").afirst() - assert message_0.message["data"]["content"] == "Hello, world!" - assert message_0.message["type"] == "human" - message_1 = await thread.messages.order_by("created_at").alast() - assert message_1.message["data"]["content"] == "Hi! How are you?" - assert message_1.message["type"] == "ai" - assert await other_thread.messages.acount() == 0 - - -def test_remove_messages(thread_aaa, thread_bbb): - thread = Thread.objects.get(name="AAA") - other_thread = Thread.objects.get(name="BBB") - - Message.objects.bulk_create( - [ - Message(thread=thread, message={"data": {"content": "Hello, world!"}, "type": "human"}), - Message(thread=thread, message={"data": {"content": "Hi! How are you?"}, "type": "ai"}), - Message(thread=other_thread, message={"data": {"content": "Olá!"}, "type": "human"}), - Message( - thread=other_thread, message={"data": {"content": "Olá! Como vai?"}, "type": "ai"} - ), - Message( - thread=other_thread, - message={"data": {"content": "Bem, está quente em Recife?"}, "type": "human"}, - ), - ] - ) - - assert thread.messages.count() == 2 - - messages = thread.messages.order_by("created_at") - message_history = DjangoChatMessageHistory(thread_id=thread.id) - message_history.remove_messages([messages[0].id]) - - assert messages.count() == 1 - assert messages.first().message["data"]["content"] == "Hi! How are you?" - assert other_thread.messages.count() == 3 - - -@pytest.mark.asyncio -@pytest.mark.django_db(transaction=True) -async def test_aremove_messages(thread_aaa, thread_bbb): - thread = await Thread.objects.aget(name="AAA") - other_thread = await Thread.objects.aget(name="BBB") - - await Message.objects.abulk_create( - [ - Message(thread=thread, message={"data": {"content": "Hello, world!"}, "type": "human"}), - Message(thread=thread, message={"data": {"content": "Hi! How are you?"}, "type": "ai"}), - Message(thread=other_thread, message={"data": {"content": "Olá!"}, "type": "human"}), - Message( - thread=other_thread, message={"data": {"content": "Olá! Como vai?"}, "type": "ai"} - ), - Message( - thread=other_thread, - message={"data": {"content": "Bem, está quente em Recife?"}, "type": "human"}, - ), - ] - ) - - assert await thread.messages.acount() == 2 - - message_history = DjangoChatMessageHistory(thread_id=thread.id) - await message_history.aremove_messages( - [(await thread.messages.order_by("created_at").afirst()).id] - ) - - assert await thread.messages.acount() == 1 - assert (await thread.messages.order_by("created_at").afirst()).message["data"][ - "content" - ] == "Hi! How are you?" - assert await other_thread.messages.acount() == 3 - - -def test_get_messages(thread_aaa, thread_bbb): - thread = Thread.objects.get(name="AAA") - other_thread = Thread.objects.get(name="BBB") - - Message.objects.bulk_create( - [ - Message(thread=thread, message={"data": {"content": "Hello, world!"}, "type": "human"}), - Message(thread=thread, message={"data": {"content": "Hi! How are you?"}, "type": "ai"}), - Message(thread=other_thread, message={"data": {"content": "Olá!"}, "type": "human"}), - Message( - thread=other_thread, message={"data": {"content": "Olá! Como vai?"}, "type": "ai"} - ), - Message( - thread=other_thread, - message={"data": {"content": "Bem, está quente em Recife?"}, "type": "human"}, - ), - ] - ) - - message_history = DjangoChatMessageHistory(thread_id=thread.id) - messages = message_history.get_messages() - other_message_history = DjangoChatMessageHistory(thread_id=other_thread.id) - other_messages = other_message_history.get_messages() - - assert len(messages) == 2 - assert messages[0].content == "Hello, world!" - assert messages[1].content == "Hi! How are you?" - assert len(other_messages) == 3 - assert other_messages[0].content == "Olá!" - assert other_messages[1].content == "Olá! Como vai?" - assert other_messages[2].content == "Bem, está quente em Recife?" - - # Test property: - assert message_history.messages == messages - assert other_message_history.messages == other_messages - - -@pytest.mark.asyncio -@pytest.mark.django_db(transaction=True) -async def test_aget_messages(thread_aaa, thread_bbb): - thread = await Thread.objects.aget(name="AAA") - other_thread = await Thread.objects.aget(name="BBB") - - await Message.objects.abulk_create( - [ - Message(thread=thread, message={"data": {"content": "Hello, world!"}, "type": "human"}), - Message(thread=thread, message={"data": {"content": "Hi! How are you?"}, "type": "ai"}), - Message(thread=other_thread, message={"data": {"content": "Olá!"}, "type": "human"}), - Message( - thread=other_thread, message={"data": {"content": "Olá! Como vai?"}, "type": "ai"} - ), - Message( - thread=other_thread, - message={"data": {"content": "Bem, está quente em Recife?"}, "type": "human"}, - ), - ] - ) - - message_history = DjangoChatMessageHistory(thread_id=thread.id) - messages = await message_history.aget_messages() - other_message_history = DjangoChatMessageHistory(thread_id=other_thread.id) - other_messages = await other_message_history.aget_messages() - - assert len(messages) == 2 - assert messages[0].content == "Hello, world!" - assert messages[1].content == "Hi! How are you?" - assert len(other_messages) == 3 - assert other_messages[0].content == "Olá!" - assert other_messages[1].content == "Olá! Como vai?" - assert other_messages[2].content == "Bem, está quente em Recife?" - - -def test_clear(thread_aaa, thread_bbb): - thread = Thread.objects.get(name="AAA") - other_thread = Thread.objects.get(name="BBB") - - Message.objects.bulk_create( - [ - Message(thread=thread, message={"data": {"content": "Hello, world!"}, "type": "human"}), - Message(thread=thread, message={"data": {"content": "Hi! How are you?"}, "type": "ai"}), - Message(thread=other_thread, message={"data": {"content": "Olá!"}, "type": "human"}), - Message( - thread=other_thread, message={"data": {"content": "Olá! Como vai?"}, "type": "ai"} - ), - Message( - thread=other_thread, - message={"data": {"content": "Bem, está quente em Recife?"}, "type": "human"}, - ), - ] - ) - - message_history = DjangoChatMessageHistory(thread_id=thread.id) - message_history.clear() - - assert thread.messages.count() == 0 - assert other_thread.messages.count() == 3 - - -@pytest.mark.asyncio -@pytest.mark.django_db(transaction=True) -async def test_aclear(thread_aaa, thread_bbb): - thread = await Thread.objects.aget(name="AAA") - other_thread = await Thread.objects.aget(name="BBB") - - await Message.objects.abulk_create( - [ - Message(thread=thread, message={"data": {"content": "Hello, world!"}, "type": "human"}), - Message(thread=thread, message={"data": {"content": "Hi! How are you?"}, "type": "ai"}), - Message(thread=other_thread, message={"data": {"content": "Olá!"}, "type": "human"}), - Message( - thread=other_thread, message={"data": {"content": "Olá! Como vai?"}, "type": "ai"} - ), - Message( - thread=other_thread, - message={"data": {"content": "Bem, está quente em Recife?"}, "type": "human"}, - ), - ] - ) - - message_history = DjangoChatMessageHistory(thread_id=thread.id) - await message_history.aclear() - - assert await thread.messages.acount() == 0 - assert await other_thread.messages.acount() == 3 diff --git a/tests/test_helpers/cassettes/test_assistants/test_AIAssistant_invoke.yaml b/tests/test_helpers/cassettes/test_assistants/test_AIAssistant_invoke.yaml index 8539f2f..2bd3457 100644 --- a/tests/test_helpers/cassettes/test_assistants/test_AIAssistant_invoke.yaml +++ b/tests/test_helpers/cassettes/test_assistants/test_AIAssistant_invoke.yaml @@ -2,705 +2,10 @@ interactions: - request: body: '{"messages": [{"content": "You are a temperature bot. Today is 2024-06-09.", "role": "system"}, {"content": "What is the temperature today in Recife?", "role": - "user"}], "model": "gpt-4o", "n": 1, "stream": true, "temperature": 1.0, "tools": - [{"type": "function", "function": {"name": "fetch_current_temperature", "description": - "Fetch the current temperature data for a location", "parameters": {"type": - "object", "properties": {"location": {"type": "string"}}, "required": ["location"]}}}, - {"type": "function", "function": {"name": "fetch_forecast_temperature", "description": - "Fetch the forecast temperature data for a location", "parameters": {"type": - "object", "properties": {"location": {"type": "string"}, "dt_str": {"description": - "Date in the format ''YYYY-MM-DD''", "type": "string"}}, "required": ["location", - "dt_str"]}}}]}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - authorization: - - DUMMY - connection: - - keep-alive - content-length: - - '833' - content-type: - - application/json - host: - - api.openai.com - user-agent: - - OpenAI/Python - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - body: - string: 'data: {"id":"chatcmpl-9YOjz2gX097iWZgi8QMItZDJyQYDg","object":"chat.completion.chunk","created":1717985283,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_p1iKkCYSNSXthLj1YMlCCDCc","type":"function","function":{"name":"fetch_current_temperature","arguments":""}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOjz2gX097iWZgi8QMItZDJyQYDg","object":"chat.completion.chunk","created":1717985283,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOjz2gX097iWZgi8QMItZDJyQYDg","object":"chat.completion.chunk","created":1717985283,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"location"}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOjz2gX097iWZgi8QMItZDJyQYDg","object":"chat.completion.chunk","created":1717985283,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOjz2gX097iWZgi8QMItZDJyQYDg","object":"chat.completion.chunk","created":1717985283,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Rec"}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOjz2gX097iWZgi8QMItZDJyQYDg","object":"chat.completion.chunk","created":1717985283,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"ife"}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOjz2gX097iWZgi8QMItZDJyQYDg","object":"chat.completion.chunk","created":1717985283,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOjz2gX097iWZgi8QMItZDJyQYDg","object":"chat.completion.chunk","created":1717985283,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]} - - - data: [DONE] - - - ' - headers: - Connection: - - keep-alive - Content-Type: - - text/event-stream; charset=utf-8 - Date: Sun, 09 Jun 2024 23:39:08 GMT - Server: DUMMY - Transfer-Encoding: - - chunked - status: - code: 200 - message: OK -- request: - body: '{"messages": [{"content": "You are a temperature bot. Today is 2024-06-09.", - "role": "system"}, {"content": "What is the temperature today in Recife?", "role": - "user"}, {"content": null, "role": "assistant", "tool_calls": [{"type": "function", - "id": "call_p1iKkCYSNSXthLj1YMlCCDCc", "function": {"name": "fetch_current_temperature", - "arguments": "{\"location\": \"Recife\"}"}}]}, {"content": "32 degrees Celsius", - "role": "tool", "tool_call_id": "call_p1iKkCYSNSXthLj1YMlCCDCc"}], "model": - "gpt-4o", "n": 1, "stream": true, "temperature": 1.0, "tools": [{"type": "function", - "function": {"name": "fetch_current_temperature", "description": "Fetch the - current temperature data for a location", "parameters": {"type": "object", "properties": - {"location": {"type": "string"}}, "required": ["location"]}}}, {"type": "function", - "function": {"name": "fetch_forecast_temperature", "description": "Fetch the - forecast temperature data for a location", "parameters": {"type": "object", - "properties": {"location": {"type": "string"}, "dt_str": {"description": "Date - in the format ''YYYY-MM-DD''", "type": "string"}}, "required": ["location", - "dt_str"]}}}]}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - authorization: - - DUMMY - connection: - - keep-alive - content-length: - - '1144' - content-type: - - application/json - cookie: - - DUMMY - host: - - api.openai.com - user-agent: - - OpenAI/Python - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - body: - string: 'data: {"id":"chatcmpl-9YOk0pFElCshyiVxhOyjImvJmpVsB","object":"chat.completion.chunk","created":1717985284,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk0pFElCshyiVxhOyjImvJmpVsB","object":"chat.completion.chunk","created":1717985284,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":"The"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk0pFElCshyiVxhOyjImvJmpVsB","object":"chat.completion.chunk","created":1717985284,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - current"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk0pFElCshyiVxhOyjImvJmpVsB","object":"chat.completion.chunk","created":1717985284,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - temperature"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk0pFElCshyiVxhOyjImvJmpVsB","object":"chat.completion.chunk","created":1717985284,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - in"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk0pFElCshyiVxhOyjImvJmpVsB","object":"chat.completion.chunk","created":1717985284,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - Recife"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk0pFElCshyiVxhOyjImvJmpVsB","object":"chat.completion.chunk","created":1717985284,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - today"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk0pFElCshyiVxhOyjImvJmpVsB","object":"chat.completion.chunk","created":1717985284,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - is"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk0pFElCshyiVxhOyjImvJmpVsB","object":"chat.completion.chunk","created":1717985284,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - "},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk0pFElCshyiVxhOyjImvJmpVsB","object":"chat.completion.chunk","created":1717985284,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":"32"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk0pFElCshyiVxhOyjImvJmpVsB","object":"chat.completion.chunk","created":1717985284,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - degrees"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk0pFElCshyiVxhOyjImvJmpVsB","object":"chat.completion.chunk","created":1717985284,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - Celsius"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk0pFElCshyiVxhOyjImvJmpVsB","object":"chat.completion.chunk","created":1717985284,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk0pFElCshyiVxhOyjImvJmpVsB","object":"chat.completion.chunk","created":1717985284,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - - - data: [DONE] - - - ' - headers: - Connection: - - keep-alive - Content-Type: - - text/event-stream; charset=utf-8 - Date: Sun, 09 Jun 2024 23:39:08 GMT - Server: DUMMY - Transfer-Encoding: - - chunked - status: - code: 200 - message: OK -- request: - body: '{"messages": [{"content": "You are a temperature bot. Today is 2024-06-09.", - "role": "system"}, {"content": "What is the temperature today in Recife?", "role": - "user"}, {"content": "The current temperature in Recife today is 32 degrees - Celsius.", "role": "assistant"}, {"content": "What about tomorrow?", "role": - "user"}], "model": "gpt-4o", "n": 1, "stream": true, "temperature": 1.0, "tools": - [{"type": "function", "function": {"name": "fetch_current_temperature", "description": - "Fetch the current temperature data for a location", "parameters": {"type": - "object", "properties": {"location": {"type": "string"}}, "required": ["location"]}}}, - {"type": "function", "function": {"name": "fetch_forecast_temperature", "description": - "Fetch the forecast temperature data for a location", "parameters": {"type": - "object", "properties": {"location": {"type": "string"}, "dt_str": {"description": - "Date in the format ''YYYY-MM-DD''", "type": "string"}}, "required": ["location", - "dt_str"]}}}]}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - authorization: - - DUMMY - connection: - - keep-alive - content-length: - - '986' - content-type: - - application/json - host: - - api.openai.com - user-agent: - - OpenAI/Python - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - body: - string: 'data: {"id":"chatcmpl-9YOk1VCEHt1A3VthxEIOeoNPArziZ","object":"chat.completion.chunk","created":1717985285,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"role":"assistant","content":null,"tool_calls":[{"index":0,"id":"call_KSrcMaPkoxmg8dtWoD5E9HIt","type":"function","function":{"name":"fetch_forecast_temperature","arguments":""}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk1VCEHt1A3VthxEIOeoNPArziZ","object":"chat.completion.chunk","created":1717985285,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\""}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk1VCEHt1A3VthxEIOeoNPArziZ","object":"chat.completion.chunk","created":1717985285,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"location"}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk1VCEHt1A3VthxEIOeoNPArziZ","object":"chat.completion.chunk","created":1717985285,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk1VCEHt1A3VthxEIOeoNPArziZ","object":"chat.completion.chunk","created":1717985285,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Rec"}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk1VCEHt1A3VthxEIOeoNPArziZ","object":"chat.completion.chunk","created":1717985285,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"ife"}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk1VCEHt1A3VthxEIOeoNPArziZ","object":"chat.completion.chunk","created":1717985285,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\",\""}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk1VCEHt1A3VthxEIOeoNPArziZ","object":"chat.completion.chunk","created":1717985285,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"dt"}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk1VCEHt1A3VthxEIOeoNPArziZ","object":"chat.completion.chunk","created":1717985285,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"_str"}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk1VCEHt1A3VthxEIOeoNPArziZ","object":"chat.completion.chunk","created":1717985285,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\":\""}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk1VCEHt1A3VthxEIOeoNPArziZ","object":"chat.completion.chunk","created":1717985285,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"202"}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk1VCEHt1A3VthxEIOeoNPArziZ","object":"chat.completion.chunk","created":1717985285,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"4"}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk1VCEHt1A3VthxEIOeoNPArziZ","object":"chat.completion.chunk","created":1717985285,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"-"}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk1VCEHt1A3VthxEIOeoNPArziZ","object":"chat.completion.chunk","created":1717985285,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"06"}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk1VCEHt1A3VthxEIOeoNPArziZ","object":"chat.completion.chunk","created":1717985285,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"-"}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk1VCEHt1A3VthxEIOeoNPArziZ","object":"chat.completion.chunk","created":1717985285,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"10"}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk1VCEHt1A3VthxEIOeoNPArziZ","object":"chat.completion.chunk","created":1717985285,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\"}"}}]},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk1VCEHt1A3VthxEIOeoNPArziZ","object":"chat.completion.chunk","created":1717985285,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"tool_calls"}]} - - - data: [DONE] - - - ' - headers: - Connection: - - keep-alive - Content-Type: - - text/event-stream; charset=utf-8 - Date: Sun, 09 Jun 2024 23:39:08 GMT - Server: DUMMY - Transfer-Encoding: - - chunked - status: - code: 200 - message: OK -- request: - body: '{"messages": [{"content": "You are a temperature bot. Today is 2024-06-09.", - "role": "system"}, {"content": "What is the temperature today in Recife?", "role": - "user"}, {"content": "The current temperature in Recife today is 32 degrees - Celsius.", "role": "assistant"}, {"content": "What about tomorrow?", "role": - "user"}, {"content": null, "role": "assistant", "tool_calls": [{"type": "function", - "id": "call_KSrcMaPkoxmg8dtWoD5E9HIt", "function": {"name": "fetch_forecast_temperature", - "arguments": "{\"location\": \"Recife\", \"dt_str\": \"2024-06-10\"}"}}]}, {"content": - "35 degrees Celsius", "role": "tool", "tool_call_id": "call_KSrcMaPkoxmg8dtWoD5E9HIt"}], - "model": "gpt-4o", "n": 1, "stream": true, "temperature": 1.0, "tools": [{"type": - "function", "function": {"name": "fetch_current_temperature", "description": - "Fetch the current temperature data for a location", "parameters": {"type": - "object", "properties": {"location": {"type": "string"}}, "required": ["location"]}}}, - {"type": "function", "function": {"name": "fetch_forecast_temperature", "description": - "Fetch the forecast temperature data for a location", "parameters": {"type": - "object", "properties": {"location": {"type": "string"}, "dt_str": {"description": - "Date in the format ''YYYY-MM-DD''", "type": "string"}}, "required": ["location", - "dt_str"]}}}]}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - authorization: - - DUMMY - connection: - - keep-alive - content-length: - - '1326' - content-type: - - application/json - cookie: - - DUMMY - host: - - api.openai.com - user-agent: - - OpenAI/Python - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - body: - string: 'data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":"The"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - forecast"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":"ed"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - temperature"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - in"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - Recife"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - for"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - tomorrow"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - June"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - "},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":"10"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - "},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":"202"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":"4"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - is"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - expected"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - to"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - be"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - "},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":"35"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - degrees"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":" - Celsius"},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}]} - - - data: {"id":"chatcmpl-9YOk2POCj2Ci9LSilXBPkyzDSuYNL","object":"chat.completion.chunk","created":1717985286,"model":"gpt-4o-2024-05-13","system_fingerprint":"fp_319be4768e","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}]} - - - data: [DONE] - - - ' - headers: - Connection: - - keep-alive - Content-Type: - - text/event-stream; charset=utf-8 - Date: Sun, 09 Jun 2024 23:39:08 GMT - Server: DUMMY - Transfer-Encoding: - - chunked - status: - code: 200 - message: OK -- request: - body: '{"messages": [{"content": "You are a temperature bot. Today is 2024-06-09.", - "role": "system"}, {"content": "What is the temperature today in Recife?", "role": - "user"}], "model": "gpt-4o", "n": 1, "stream": false, "temperature": 1.0, "tools": - [{"type": "function", "function": {"name": "fetch_current_temperature", "description": - "Fetch the current temperature data for a location", "parameters": {"type": - "object", "properties": {"location": {"type": "string"}}, "required": ["location"]}}}, - {"type": "function", "function": {"name": "fetch_forecast_temperature", "description": - "Fetch the forecast temperature data for a location", "parameters": {"type": - "object", "properties": {"location": {"type": "string"}, "dt_str": {"description": - "Date in the format ''YYYY-MM-DD''", "type": "string"}}, "required": ["location", - "dt_str"]}}}]}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - authorization: - - DUMMY - connection: - - keep-alive - content-length: - - '834' - content-type: - - application/json - host: - - api.openai.com - user-agent: - - OpenAI/Python - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - body: - string: !!binary | - H4sIAAAAAAAAA2xSXW/TMBR9z6+w7nODkqxNu7wNOrFqgBAMIZWiyHVvPlbHtuwbxKj63ycnaZtV - 5MGyzsk55/rYh4AxqHeQMRAVJ9EYGd6lD9Hd+tOXav2I9+ZeL9d/vj+vFtMPBsV7mHiF3j6joJPq - ndCNkUi1Vj0tLHJC7xrPkzSaLdJF2hGN3qH0stJQONVhEiXTMJqF8c0grHQt0EHGfgWMMXboVj+i - 2uFfyFg0OSENOsdLhOz8E2NgtfQIcOdqR1wRTC6k0IpQ+alVK+WIIK1lLriUl+D+O4z2l564lPl+ - P58vb79Gqx9L8bTe3f7btz8/fl5tR3m99YvpBipaJc79jPgznl2FMQaKN70WSVS5aK1FRTlhY9By - ai1emTEG3JZtg4r8QeCwAakF9/YbyDbwDUVd4AaO8EZ2DP63/z3qx2LROi6H4gb8eL4JqUtj9dZd - FQtFrWpX5Ra56w447jk4pXU50L65SjBWN4Zy0ntU3jaO494VLi9txKYDSZq4HOHJPBjmBPfiCJu8 - qFWJ1ti6ewZQmDyZpcmU4w2fQXAMXgEAAP//AwAwzXr/DwMAAA== - headers: - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Type: - - application/json - Date: Sun, 09 Jun 2024 23:39:08 GMT - Server: DUMMY - Transfer-Encoding: - - chunked - X-Content-Type-Options: - - nosniff - access-control-expose-headers: - - X-Request-ID - status: - code: 200 - message: OK -- request: - body: '{"messages": [{"content": "You are a temperature bot. Today is 2024-06-09.", - "role": "system"}, {"content": "What is the temperature today in Recife?", "role": - "user"}, {"content": null, "role": "assistant", "tool_calls": [{"type": "function", - "id": "call_kk77D9P0IUDcTZd9zkuWGMIb", "function": {"name": "fetch_current_temperature", - "arguments": "{\"location\": \"Recife\"}"}}]}, {"content": "32 degrees Celsius", - "role": "tool", "tool_call_id": "call_kk77D9P0IUDcTZd9zkuWGMIb"}], "model": - "gpt-4o", "n": 1, "stream": false, "temperature": 1.0, "tools": [{"type": "function", - "function": {"name": "fetch_current_temperature", "description": "Fetch the - current temperature data for a location", "parameters": {"type": "object", "properties": - {"location": {"type": "string"}}, "required": ["location"]}}}, {"type": "function", - "function": {"name": "fetch_forecast_temperature", "description": "Fetch the - forecast temperature data for a location", "parameters": {"type": "object", - "properties": {"location": {"type": "string"}, "dt_str": {"description": "Date - in the format ''YYYY-MM-DD''", "type": "string"}}, "required": ["location", - "dt_str"]}}}]}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - authorization: - - DUMMY - connection: - - keep-alive - content-length: - - '1145' - content-type: - - application/json - cookie: - - DUMMY - host: - - api.openai.com - user-agent: - - OpenAI/Python - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - body: - string: !!binary | - H4sIAAAAAAAAAwAAAP//VJDNTsMwEITveYqVz02Vpk0KvRV6AAEXVAESQpXrbBKDY1vejURV9d1R - 0vSHiw/zeUazs48AhC7EAoSqJavGm3iZPyR31Rof6d1oJlPVL+btY/m8elqtgxh1Drf9RsUn11i5 - xhtk7ewRq4CSsUudzNM8yW7ym3kPGleg6WyV53jm4jRJZ3GSxZPpYKydVkhiAZ8RAMC+f7uKtsBf - sYBkdFIaJJIVisX5E4AIznSKkESaWFoWowtUzjLavvW6RlBtCGgZGBuPQXIbELSFV1S6RGBXyB1o - gmkKBVYBkeAeDemWxtehAcuWZHeTbY0Z9MO5pXGVD25LAz/rpbaa6k1ASc52jYidFz09RABf/Rrt - vwOFD67xvGH3g7YLnExvj3nisv81HSA7luZKz9JoaChoR4zNptS2wuCDPo5T+k2a5elM4lRmIjpE - fwAAAP//AwDhh+s+JQIAAA== - headers: - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Type: - - application/json - Date: Sun, 09 Jun 2024 23:39:08 GMT - Server: DUMMY - Transfer-Encoding: - - chunked - X-Content-Type-Options: - - nosniff - access-control-expose-headers: - - X-Request-ID - status: - code: 200 - message: OK -- request: - body: '{"messages": [{"content": "You are a temperature bot. Today is 2024-06-09.", - "role": "system"}, {"content": "What is the temperature today in Recife?", "role": - "user"}, {"content": "The current temperature in Recife today is 32 degrees - Celsius.", "role": "assistant"}, {"content": "What about tomorrow?", "role": "user"}], "model": "gpt-4o", "n": 1, "stream": false, "temperature": 1.0, "tools": [{"type": "function", "function": {"name": "fetch_current_temperature", "description": - "Fetch the current temperature data for a location", "parameters": {"type": - "object", "properties": {"location": {"type": "string"}}, "required": ["location"]}}}, - {"type": "function", "function": {"name": "fetch_forecast_temperature", "description": - "Fetch the forecast temperature data for a location", "parameters": {"type": - "object", "properties": {"location": {"type": "string"}, "dt_str": {"description": - "Date in the format ''YYYY-MM-DD''", "type": "string"}}, "required": ["location", - "dt_str"]}}}]}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - authorization: - - DUMMY - connection: - - keep-alive - content-length: - - '987' - content-type: - - application/json - host: - - api.openai.com - user-agent: - - OpenAI/Python - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - body: - string: !!binary | - H4sIAAAAAAAAAwAAAP//bFLLbtswELzrK4g9W4UkW4qhW1O0yKFB0wBB01aFwFAriS5FsuQKrWH4 - 3ws9bCtGeCCIGc7scpaHgDGQFeQMRMtJdFaF77O76MOzT+7aB70RT38e73eYVJZi/Hb/EVaDwrzs - UNBJ9U6YziokafREC4eccHCNb5IsSrfZdjsSnalQDbLGUrgxYRIlmzBKw3g9C1sjBXrI2c+AMcYO - 4z60qCv8BzmLViekQ+95g5CfLzEGzqgBAe699MQ1wepCCqMJ9dC17pVaEGSMKgVX6lJ4WofF+ZIT - V6rsflSf43qffM9Maqvd36fbr18ePj3LRb3Jem/Hhupei3M+C/6M51fFGAPNu0mLJNqyNg4F91QS - dhYdp97hlRtjwF3Td6hpeAkcClBG8MG/gLyARxSyxgJWBVRUenIjOs0gC+OogCO8MjwGb51/LaJz - WPeeqznTGT+eh6RMY5158VeZQy219G3pkPvx7csRBKdqYx3oX00ZrDOdpZLMb9SDbbxOJ1e4fMIL - m9zMJBniaqHKkmDuE/zeE3ZlLXWDzjo5/hCobZmkWbLhuOYpBMfgPwAAAP//AwD589dpKgMAAA== - headers: - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Type: - - application/json - Date: Sun, 09 Jun 2024 23:39:08 GMT - Server: DUMMY - Transfer-Encoding: - - chunked - X-Content-Type-Options: - - nosniff - access-control-expose-headers: - - X-Request-ID - status: - code: 200 - message: OK -- request: - body: '{"messages": [{"content": "You are a temperature bot. Today is 2024-06-09.", - "role": "system"}, {"content": "What is the temperature today in Recife?", "role": - "user"}, {"content": "The current temperature in Recife today is 32 degrees - Celsius.", "role": "assistant"}, {"content": "What about tomorrow?", "role": - "user"}, {"content": null, "role": "assistant", "tool_calls": [{"type": "function", - "id": "call_mZdL1fy2Y6o5pdjwUBQOPFXi", "function": {"name": "fetch_forecast_temperature", - "arguments": "{\"location\": \"Recife\", \"dt_str\": \"2024-06-10\"}"}}]}, {"content": - "35 degrees Celsius", "role": "tool", "tool_call_id": "call_mZdL1fy2Y6o5pdjwUBQOPFXi"}], - "model": "gpt-4o", "n": 1, "stream": false, "temperature": 1.0, "tools": [{"type": - "function", "function": {"name": "fetch_current_temperature", "description": - "Fetch the current temperature data for a location", "parameters": {"type": - "object", "properties": {"location": {"type": "string"}}, "required": ["location"]}}}, - {"type": "function", "function": {"name": "fetch_forecast_temperature", "description": - "Fetch the forecast temperature data for a location", "parameters": {"type": - "object", "properties": {"location": {"type": "string"}, "dt_str": {"description": - "Date in the format ''YYYY-MM-DD''", "type": "string"}}, "required": ["location", - "dt_str"]}}}]}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - authorization: - - DUMMY - connection: - - keep-alive - content-length: - - '1327' - content-type: - - application/json - cookie: - - DUMMY - host: - - api.openai.com - user-agent: - - OpenAI/Python - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - body: - string: !!binary | - H4sIAAAAAAAAA1SRS0/DMBCE7/kVK58bFNKmtL1FRQguCCE4IIQq19mkBsdreTc8hPrfUdInFx/m - 21nNjn8TAGUrtQBlNlpMG1xaTm+za/P8YGf2Hpd3y7J4+dRYdjcPpe/UqHfQ+h2NHFwXhtrgUCz5 - HTYRtWC/9fIqn2bFbDqbD6ClCl1va4KkE0rzLJ+kWZFejvfGDVmDrBbwmgAA/A5vH9FX+K0WkI0O - SovMukG1OA4BqEiuV5RmtizaixqdoCEv6IfUTxuEmiIazYIVCLYBo5YuDjIItRQjfYH18IjG1giW - YVxAhU1EZFiiY9vxxfn6iHXHur/Od87t9e0xr6MmRFrznh/12nrLm1VEzeT7bCwU1EC3CcDb0Ev3 - 71QVIrVBVkIf6HloudjtU6efOKMHKCTanenzLNknVPzDgu2qtr7BGKLd1VSHVV5M84nGsS5Usk3+ - AAAA//8DADVkeHYvAgAA - headers: - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Type: - - application/json - Date: Sun, 09 Jun 2024 23:39:08 GMT - Server: DUMMY - Transfer-Encoding: - - chunked - X-Content-Type-Options: - - nosniff - access-control-expose-headers: - - X-Request-ID - status: - code: 200 - message: OK -- request: - body: '{"messages": [{"content": "You are a temperature bot. Today is 2024-06-09.", - "role": "system"}, {"content": "What is the temperature today in Recife?", "role": - "user"}], "model": "gpt-4o", "n": 1, "stream": false, "temperature": 1.0, "tools": - [{"type": "function", "function": {"name": "fetch_current_temperature", "description": - "Fetch the current temperature data for a location", "parameters": {"type": - "object", "properties": {"location": {"type": "string"}}, "required": ["location"]}}}, + "Fetch the current temperature data for a location", "parameters": {"properties": + {"location": {"type": "string"}}, "required": ["location"], "type": "object"}}}, {"type": "function", "function": {"name": "fetch_forecast_temperature", "description": "Fetch the forecast temperature data for a location", "parameters": {"type": "object", "properties": {"location": {"type": "string"}, "dt_str": {"description": @@ -728,18 +33,19 @@ interactions: method: POST uri: https://api.openai.com/v1/chat/completions response: - content: "{\n \"id\": \"chatcmpl-ABSmPJz6yUnuKDn01OjKlvgTJrwCx\",\n \"object\": - \"chat.completion\",\n \"created\": 1727295601,\n \"model\": \"gpt-4o-2024-05-13\",\n + content: "{\n \"id\": \"chatcmpl-AEgz9DB9DxR3nsIIJ4zsMnWD4haXy\",\n \"object\": + \"chat.completion\",\n \"created\": 1728065191,\n \"model\": \"gpt-4o-2024-08-06\",\n \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": \"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n - \ \"id\": \"call_Nh0wFfZFLfYsn73Lq9KUHDb9\",\n \"type\": + \ \"id\": \"call_mp680g1ciZRb9eaRoWZUpMWG\",\n \"type\": \"function\",\n \"function\": {\n \"name\": \"fetch_current_temperature\",\n \ \"arguments\": \"{\\\"location\\\":\\\"Recife\\\"}\"\n }\n \ }\n ],\n \"refusal\": null\n },\n \"logprobs\": null,\n \"finish_reason\": \"tool_calls\"\n }\n ],\n \"usage\": {\n \ \"prompt_tokens\": 111,\n \"completion_tokens\": 16,\n \"total_tokens\": - 127,\n \"completion_tokens_details\": {\n \"reasoning_tokens\": 0\n - \ }\n },\n \"system_fingerprint\": \"fp_e375328146\"\n}\n" + 127,\n \"prompt_tokens_details\": {\n \"cached_tokens\": 0\n },\n + \ \"completion_tokens_details\": {\n \"reasoning_tokens\": 0\n }\n + \ },\n \"system_fingerprint\": \"fp_e5e4913e83\"\n}\n" headers: Connection: - keep-alive @@ -761,17 +67,17 @@ interactions: body: '{"messages": [{"content": "You are a temperature bot. Today is 2024-06-09.", "role": "system"}, {"content": "What is the temperature today in Recife?", "role": "user"}, {"content": null, "role": "assistant", "tool_calls": [{"type": "function", - "id": "call_Nh0wFfZFLfYsn73Lq9KUHDb9", "function": {"name": "fetch_current_temperature", + "id": "call_mp680g1ciZRb9eaRoWZUpMWG", "function": {"name": "fetch_current_temperature", "arguments": "{\"location\": \"Recife\"}"}}]}, {"content": "32 degrees Celsius", - "role": "tool", "tool_call_id": "call_Nh0wFfZFLfYsn73Lq9KUHDb9"}], "model": + "role": "tool", "tool_call_id": "call_mp680g1ciZRb9eaRoWZUpMWG"}], "model": "gpt-4o", "n": 1, "stream": false, "temperature": 1.0, "tools": [{"type": "function", "function": {"name": "fetch_current_temperature", "description": "Fetch the - current temperature data for a location", "parameters": {"type": "object", "properties": - {"location": {"type": "string"}}, "required": ["location"]}}}, {"type": "function", - "function": {"name": "fetch_forecast_temperature", "description": "Fetch the - forecast temperature data for a location", "parameters": {"type": "object", - "properties": {"location": {"type": "string"}, "dt_str": {"description": "Date - in the format ''YYYY-MM-DD''", "type": "string"}}, "required": ["location", + current temperature data for a location", "parameters": {"properties": {"location": + {"type": "string"}}, "required": ["location"], "type": "object"}}}, {"type": + "function", "function": {"name": "fetch_forecast_temperature", "description": + "Fetch the forecast temperature data for a location", "parameters": {"type": + "object", "properties": {"location": {"type": "string"}, "dt_str": {"description": + "Date in the format ''YYYY-MM-DD''", "type": "string"}}, "required": ["location", "dt_str"]}}}]}' headers: accept: @@ -797,14 +103,15 @@ interactions: method: POST uri: https://api.openai.com/v1/chat/completions response: - content: "{\n \"id\": \"chatcmpl-ABSmQenFKnMlYo3HgGeVbDUhrugK6\",\n \"object\": - \"chat.completion\",\n \"created\": 1727295602,\n \"model\": \"gpt-4o-2024-05-13\",\n + content: "{\n \"id\": \"chatcmpl-AEgzAn98eSbe5BCydWXLETWJ2qcgv\",\n \"object\": + \"chat.completion\",\n \"created\": 1728065192,\n \"model\": \"gpt-4o-2024-08-06\",\n \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": - \"assistant\",\n \"content\": \"The current temperature today in Recife - is 32 degrees Celsius.\",\n \"refusal\": null\n },\n \"logprobs\": + \"assistant\",\n \"content\": \"The current temperature in Recife is + 32 degrees Celsius.\",\n \"refusal\": null\n },\n \"logprobs\": null,\n \"finish_reason\": \"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\": - 139,\n \"completion_tokens\": 13,\n \"total_tokens\": 152,\n \"completion_tokens_details\": - {\n \"reasoning_tokens\": 0\n }\n },\n \"system_fingerprint\": \"fp_52a7f40b0b\"\n}\n" + 139,\n \"completion_tokens\": 12,\n \"total_tokens\": 151,\n \"prompt_tokens_details\": + {\n \"cached_tokens\": 0\n },\n \"completion_tokens_details\": {\n + \ \"reasoning_tokens\": 0\n }\n },\n \"system_fingerprint\": \"fp_e5e4913e83\"\n}\n" headers: Connection: - keep-alive @@ -825,17 +132,20 @@ interactions: - request: body: '{"messages": [{"content": "You are a temperature bot. Today is 2024-06-09.", "role": "system"}, {"content": "What is the temperature today in Recife?", "role": - "user"}, {"content": "The current temperature today in Recife is 32 degrees - Celsius.", "role": "assistant"}, {"content": "What about tomorrow?", "role": - "user"}], "model": "gpt-4o", "n": 1, "stream": false, "temperature": 1.0, "tools": - [{"type": "function", "function": {"name": "fetch_current_temperature", "description": - "Fetch the current temperature data for a location", "parameters": {"type": - "object", "properties": {"location": {"type": "string"}}, "required": ["location"]}}}, - {"type": "function", "function": {"name": "fetch_forecast_temperature", "description": - "Fetch the forecast temperature data for a location", "parameters": {"type": - "object", "properties": {"location": {"type": "string"}, "dt_str": {"description": - "Date in the format ''YYYY-MM-DD''", "type": "string"}}, "required": ["location", - "dt_str"]}}}]}' + "user"}, {"content": null, "role": "assistant", "tool_calls": [{"type": "function", + "id": "call_mp680g1ciZRb9eaRoWZUpMWG", "function": {"name": "fetch_current_temperature", + "arguments": "{\"location\": \"Recife\"}"}}]}, {"content": "32 degrees Celsius", + "role": "tool", "tool_call_id": "call_mp680g1ciZRb9eaRoWZUpMWG"}, {"content": + "The current temperature in Recife is 32 degrees Celsius.", "role": "assistant"}, + {"content": "What about tomorrow?", "role": "user"}], "model": "gpt-4o", "n": + 1, "stream": false, "temperature": 1.0, "tools": [{"type": "function", "function": + {"name": "fetch_current_temperature", "description": "Fetch the current temperature + data for a location", "parameters": {"properties": {"location": {"type": "string"}}, + "required": ["location"], "type": "object"}}}, {"type": "function", "function": + {"name": "fetch_forecast_temperature", "description": "Fetch the forecast temperature + data for a location", "parameters": {"type": "object", "properties": {"location": + {"type": "string"}, "dt_str": {"description": "Date in the format ''YYYY-MM-DD''", + "type": "string"}}, "required": ["location", "dt_str"]}}}]}' headers: accept: - application/json @@ -846,7 +156,7 @@ interactions: connection: - keep-alive content-length: - - '987' + - '1292' content-type: - application/json host: @@ -858,18 +168,19 @@ interactions: method: POST uri: https://api.openai.com/v1/chat/completions response: - content: "{\n \"id\": \"chatcmpl-ABSmRXTcmDPDXZDygDWQVxwnsboZ5\",\n \"object\": - \"chat.completion\",\n \"created\": 1727295603,\n \"model\": \"gpt-4o-2024-05-13\",\n + content: "{\n \"id\": \"chatcmpl-AEgzBghCShRXpHt6arswuiOlibksr\",\n \"object\": + \"chat.completion\",\n \"created\": 1728065193,\n \"model\": \"gpt-4o-2024-08-06\",\n \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": \"assistant\",\n \"content\": null,\n \"tool_calls\": [\n {\n - \ \"id\": \"call_8ZyKSMI0hu5t3lM0ZzlCM2iJ\",\n \"type\": + \ \"id\": \"call_5Y5P4y5m0VFzh5GE0RNeEkyP\",\n \"type\": \"function\",\n \"function\": {\n \"name\": \"fetch_forecast_temperature\",\n \ \"arguments\": \"{\\\"location\\\":\\\"Recife\\\",\\\"dt_str\\\":\\\"2024-06-10\\\"}\"\n \ }\n }\n ],\n \"refusal\": null\n },\n \ \"logprobs\": null,\n \"finish_reason\": \"tool_calls\"\n }\n - \ ],\n \"usage\": {\n \"prompt_tokens\": 135,\n \"completion_tokens\": - 27,\n \"total_tokens\": 162,\n \"completion_tokens_details\": {\n \"reasoning_tokens\": - 0\n }\n },\n \"system_fingerprint\": \"fp_e375328146\"\n}\n" + \ ],\n \"usage\": {\n \"prompt_tokens\": 162,\n \"completion_tokens\": + 27,\n \"total_tokens\": 189,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0\n },\n \"completion_tokens_details\": {\n \"reasoning_tokens\": + 0\n }\n },\n \"system_fingerprint\": \"fp_e5e4913e83\"\n}\n" headers: Connection: - keep-alive @@ -890,17 +201,21 @@ interactions: - request: body: '{"messages": [{"content": "You are a temperature bot. Today is 2024-06-09.", "role": "system"}, {"content": "What is the temperature today in Recife?", "role": - "user"}, {"content": "The current temperature today in Recife is 32 degrees - Celsius.", "role": "assistant"}, {"content": "What about tomorrow?", "role": "user"}, {"content": null, "role": "assistant", "tool_calls": [{"type": "function", - "id": "call_8ZyKSMI0hu5t3lM0ZzlCM2iJ", "function": {"name": "fetch_forecast_temperature", - "arguments": "{\"location\": \"Recife\", \"dt_str\": \"2024-06-10\"}"}}]}, {"content": - "35 degrees Celsius", "role": "tool", "tool_call_id": "call_8ZyKSMI0hu5t3lM0ZzlCM2iJ"}], - "model": "gpt-4o", "n": 1, "stream": false, "temperature": 1.0, "tools": [{"type": - "function", "function": {"name": "fetch_current_temperature", "description": - "Fetch the current temperature data for a location", "parameters": {"type": - "object", "properties": {"location": {"type": "string"}}, "required": ["location"]}}}, - {"type": "function", "function": {"name": "fetch_forecast_temperature", "description": + "id": "call_mp680g1ciZRb9eaRoWZUpMWG", "function": {"name": "fetch_current_temperature", + "arguments": "{\"location\": \"Recife\"}"}}]}, {"content": "32 degrees Celsius", + "role": "tool", "tool_call_id": "call_mp680g1ciZRb9eaRoWZUpMWG"}, {"content": + "The current temperature in Recife is 32 degrees Celsius.", "role": "assistant"}, + {"content": "What about tomorrow?", "role": "user"}, {"content": null, "role": + "assistant", "tool_calls": [{"type": "function", "id": "call_5Y5P4y5m0VFzh5GE0RNeEkyP", + "function": {"name": "fetch_forecast_temperature", "arguments": "{\"location\": + \"Recife\", \"dt_str\": \"2024-06-10\"}"}}]}, {"content": "35 degrees Celsius", + "role": "tool", "tool_call_id": "call_5Y5P4y5m0VFzh5GE0RNeEkyP"}], "model": + "gpt-4o", "n": 1, "stream": false, "temperature": 1.0, "tools": [{"type": "function", + "function": {"name": "fetch_current_temperature", "description": "Fetch the + current temperature data for a location", "parameters": {"properties": {"location": + {"type": "string"}}, "required": ["location"], "type": "object"}}}, {"type": + "function", "function": {"name": "fetch_forecast_temperature", "description": "Fetch the forecast temperature data for a location", "parameters": {"type": "object", "properties": {"location": {"type": "string"}, "dt_str": {"description": "Date in the format ''YYYY-MM-DD''", "type": "string"}}, "required": ["location", @@ -915,7 +230,7 @@ interactions: connection: - keep-alive content-length: - - '1327' + - '1632' content-type: - application/json cookie: @@ -929,15 +244,15 @@ interactions: method: POST uri: https://api.openai.com/v1/chat/completions response: - content: "{\n \"id\": \"chatcmpl-ABSmRcNX92uvRDsLmhvANFe5RUt7W\",\n \"object\": - \"chat.completion\",\n \"created\": 1727295603,\n \"model\": \"gpt-4o-2024-05-13\",\n + content: "{\n \"id\": \"chatcmpl-AEgzCNv5MYIGUAglgxEiD0FPbdNAf\",\n \"object\": + \"chat.completion\",\n \"created\": 1728065194,\n \"model\": \"gpt-4o-2024-08-06\",\n \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": - \"assistant\",\n \"content\": \"The forecasted temperature in Recife - for tomorrow, June 10th, is 35 degrees Celsius.\",\n \"refusal\": null\n - \ },\n \"logprobs\": null,\n \"finish_reason\": \"stop\"\n }\n - \ ],\n \"usage\": {\n \"prompt_tokens\": 175,\n \"completion_tokens\": - 21,\n \"total_tokens\": 196,\n \"completion_tokens_details\": {\n \"reasoning_tokens\": - 0\n }\n },\n \"system_fingerprint\": \"fp_3537616b13\"\n}\n" + \"assistant\",\n \"content\": \"The forecasted temperature for tomorrow + in Recife is 35 degrees Celsius.\",\n \"refusal\": null\n },\n \"logprobs\": + null,\n \"finish_reason\": \"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\": + 202,\n \"completion_tokens\": 15,\n \"total_tokens\": 217,\n \"prompt_tokens_details\": + {\n \"cached_tokens\": 0\n },\n \"completion_tokens_details\": {\n + \ \"reasoning_tokens\": 0\n }\n },\n \"system_fingerprint\": \"fp_e5e4913e83\"\n}\n" headers: Connection: - keep-alive diff --git a/tests/test_helpers/cassettes/test_assistants/test_AIAssistant_with_rag_invoke.yaml b/tests/test_helpers/cassettes/test_assistants/test_AIAssistant_with_rag_invoke.yaml index 64ca1a5..9bf5ca3 100644 --- a/tests/test_helpers/cassettes/test_assistants/test_AIAssistant_with_rag_invoke.yaml +++ b/tests/test_helpers/cassettes/test_assistants/test_AIAssistant_with_rag_invoke.yaml @@ -1,436 +1,4 @@ interactions: -- request: - body: '{"messages": [{"content": "Given a chat history and the latest user question - which might reference context in the chat history, formulate a standalone question - which can be understood without the chat history. Do NOT answer the question, - just reformulate it if needed and otherwise return it as is.", "role": "system"}, - {"content": "You are a tour guide assistant offers information about nearby - attractions. The user is at a location and wants to know what to learn about - nearby attractions. Use the following pieces of context to suggest nearby attractions - to the user. If there are no interesting attractions nearby, tell the user there''s - nothing to see where they''re at. Use three sentences maximum and keep your - suggestions concise.", "role": "system"}, {"content": "I''m at Central Park - W & 79st, New York, NY 10024, United States.", "role": "user"}], "model": "gpt-4o", - "n": 1, "stream": false, "temperature": 1.0}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - authorization: - - DUMMY - connection: - - keep-alive - content-length: - - '920' - content-type: - - application/json - host: - - api.openai.com - user-agent: - - OpenAI/Python - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - body: - string: !!binary | - H4sIAAAAAAAAAwAAAP//VJHBbtswEETv+ooFDz1JhaRIiu1LUfScIKhRBEZRGGtpLdGmSIK7bpIG - /veCsmqnFx7mcYazy/cEQOlOrUC1A0o7epN9bQ7VxnZr0X+qesfrfnGs9Oa3+X54WDYqjQ63O1Ar - /1yfWzd6Q6KdveA2EArF1OK+bIqmqZf5BEbXkYm23ktWuazMyyrL66y4m42D0y2xWsHPBADgfTpj - RdvRq1rBFDMpIzFjT2p1vQSggjNRUcisWdCKSm+wdVbITq2fBxTAQMBuJLCEYfcGKBKwjVMwoMA3 - shLQwBOGIzwTC3yC+6UMsJZAJCk80gtsXDim8LiBIs/LKoUfVgt1sBYU4i8fXw+0PzHG4e3JmFk/ - X8cxrvfB7XjmV32vreZhGwjZ2VidxXk10XMC8Gta2+m/TSgf3OhlK+5INgYW9eKSp24fdaNlM0Nx - guaDa1Elc0PFbyw0bvfa9hR80Jct7v22rJuyQrrDWiXn5C8AAAD//wMAk6nwpU4CAAA= - headers: - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Type: - - application/json - Date: Sun, 09 Jun 2024 23:39:08 GMT - Server: DUMMY - Transfer-Encoding: - - chunked - X-Content-Type-Options: - - nosniff - access-control-expose-headers: - - X-Request-ID - status: - code: 200 - message: OK -- request: - body: '{"messages": [{"content": "You are a tour guide assistant offers information - about nearby attractions. The user is at a location and wants to know what to - learn about nearby attractions. Use the following pieces of context to suggest - nearby attractions to the user. If there are no interesting attractions nearby, - tell the user there''s nothing to see where they''re at. Use three sentences - maximum and keep your suggestions concise.", "role": "system"}, {"content": - "I''m at Central Park W & 79st, New York, NY 10024, United States.", "role": - "user"}, {"content": "---START OF CONTEXT---\nCentral Park\n\nAmerican Museum - of Natural History---END OF CONTEXT---\n", "role": "system"}], "model": "gpt-4o", - "n": 1, "stream": false, "temperature": 1.0}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - authorization: - - DUMMY - connection: - - keep-alive - content-length: - - '745' - content-type: - - application/json - host: - - api.openai.com - user-agent: - - OpenAI/Python - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - body: - string: !!binary | - H4sIAAAAAAAAAwAAAP//VFLLjhoxELzzFS1fchkQwwIh3EikCCXK6xIpG0XImB7Gi8c96m7zyIp/ - jzywsLn4UOWqLrv6uQdg/MbMwbjaqmva0F9Mn8aPp58/9suDEn7++3E/fvxWLvC0PNLYFFlB6yd0 - +qIaOGragOopXmjHaBWza/l2NC2n08m7siMa2mDIsm2r/TH1R8PRuD+c9MuHq7Am71DMHH73AACe - uzNHjBs8mjkMixekQRG7RTO/XQIwTCEjxop4URvVFHfSUVSMXepflN4wAvttrbA+wQeMyjbAd8u7 - AlrkCp1CRQwWxGH0Dg427KAD9l68ghJ4FahsQ0kg2LhpLO9kAJ+SKFjHJAJaI4gyohZwogTORsBj - G4ix4xYNss/glySYGqAKvlpNOcrSixKfCqipwTytsuJ8tOrjFvBY+3UeTxHq1NgILgVNjFJ0vvFq - ciAOmwJs3HRwin6PLDiA96Q1UFVhfhB7V+dYyB6jQ/DxYoCdsL4EGbz+SsYqic1NxhTCFT/fugm0 - bZnWcuVveOWjl3rFaIVi7kGUWtOx5x7An24H0n+1mpapaXWltMOYDcvR7OJn7lt3Z6fXBTFKasMr - 1WzWuyY0chLFZlX5uEVu2V9WompXo8l0NLb4YCemd+79AwAA//8DALP7VSgbAwAA - headers: - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Type: - - application/json - Date: Sun, 09 Jun 2024 23:39:08 GMT - Server: DUMMY - Transfer-Encoding: - - chunked - X-Content-Type-Options: - - nosniff - access-control-expose-headers: - - X-Request-ID - status: - code: 200 - message: OK -- request: - body: '{"messages": [{"content": "Given a chat history and the latest user question - which might reference context in the chat history, formulate a standalone question - which can be understood without the chat history. Do NOT answer the question, - just reformulate it if needed and otherwise return it as is.", "role": "system"}, - {"content": "You are a tour guide assistant offers information about nearby - attractions. The user is at a location and wants to know what to learn about - nearby attractions. Use the following pieces of context to suggest nearby attractions - to the user. If there are no interesting attractions nearby, tell the user there''s - nothing to see where they''re at. Use three sentences maximum and keep your - suggestions concise.", "role": "system"}, {"content": "I''m at Central Park - W & 79st, New York, NY 10024, United States.", "role": "user"}, {"content": - "You''re right by Central Park, perfect for a scenic walk or a visit to its - famous landmarks. Just across the street, you can explore the American Museum - of Natural History, home to fascinating exhibits on human cultures, the natural - world, and the universe. Both offer a rich experience in nature and history.", - "role": "assistant"}, {"content": "11 W 53rd St, New York, NY 10019, United - States.", "role": "user"}], "model": "gpt-4o", "n": 1, "stream": false, "temperature": - 1.0}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - authorization: - - DUMMY - connection: - - keep-alive - content-length: - - '1347' - content-type: - - application/json - host: - - api.openai.com - user-agent: - - OpenAI/Python - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - body: - string: !!binary | - H4sIAAAAAAAAA1SRwW7bMBBE7/qKBc9SIdGSDPtSNMccXBRFawRFYWyklcyE4hLkGmkb+N8Lyorj - XHiYxxnOLl8zAGV6tQXVHVG6ydviS/tUo+V/sR92Y6m/6nb/cv9tszbru59B5cnBj0/UyZvrU8eT - tySG3QV3gVAopVZr3VZt22z0DCbuySbb6KWoudClrouyKarVYjyy6SiqLfzKAABe5zNVdD39UVso - 8zdlohhxJLW9XgJQgW1SFMZooqATlb/Djp2Qm1vvjyiAIgG71DoCBgJHGKCqYA/NKvTwXXLY0Qs8 - cHjOYfcAVVlWmxx+OCOUMArFz7cPBBpOEdN87mTtop+vjS2PPvBjXPhVH4wz8XgIhJFdaheFvZrp - OQP4PW/m9GFY5QNPXg7Cz+RSoK7rS556/4sbulqgsKC90dt1tjRU8W8Umg6DcSMFH8xlUYM/6KbV - NdIKG5Wds/8AAAD//wMAji1wrTECAAA= - headers: - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Type: - - application/json - Date: Sun, 09 Jun 2024 23:39:08 GMT - Server: DUMMY - Transfer-Encoding: - - chunked - X-Content-Type-Options: - - nosniff - access-control-expose-headers: - - X-Request-ID - status: - code: 200 - message: OK -- request: - body: '{"messages": [{"content": "You are a tour guide assistant offers information - about nearby attractions. The user is at a location and wants to know what to - learn about nearby attractions. Use the following pieces of context to suggest - nearby attractions to the user. If there are no interesting attractions nearby, - tell the user there''s nothing to see where they''re at. Use three sentences - maximum and keep your suggestions concise.", "role": "system"}, {"content": - "I''m at Central Park W & 79st, New York, NY 10024, United States.", "role": - "user"}, {"content": "You''re right by Central Park, perfect for a scenic walk - or a visit to its famous landmarks. Just across the street, you can explore - the American Museum of Natural History, home to fascinating exhibits on human - cultures, the natural world, and the universe. Both offer a rich experience - in nature and history.", "role": "assistant"}, {"content": "11 W 53rd St, New - York, NY 10019, United States.", "role": "user"}, {"content": "---START OF CONTEXT---\nCentral - Park\n\nAmerican Museum of Natural History---END OF CONTEXT---\n", "role": "system"}], - "model": "gpt-4o", "n": 1, "stream": false, "temperature": 1.0}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - authorization: - - DUMMY - connection: - - keep-alive - content-length: - - '1172' - content-type: - - application/json - host: - - api.openai.com - user-agent: - - OpenAI/Python - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - body: - string: !!binary | - H4sIAAAAAAAAA1RSTYsaQRC9+yuKviSBUdRVl/VmEnKKEMJCyIYgbU+N09rT1VRV68qy/z306H7k - 0jT16r169fE0ADC+NkswrrXquhSGq8V+tn1ox/u7+P3X9m772K6+fru9H4/z9kdrqsKg7R6dvrBG - jroUUD3FC+wYrWJRndxOF5PFYn530wMd1RgKbZd0OKPhdDydDcfz4eTmSmzJOxSzhD8DAICn/i0W - Y42PZgnj6iXSoYjdoVm+JgEYplAixop4URvVVG+go6gYe9e/KX9gBPa7ViGiZdAWYZ0FcwfUwJpq - 5AgrVvi4pvXqUwWn1rsWWsqCAjaC7xKjiD8iOAoBXWm/cLsL18Ya+opdIrZ8Bss6glVd+5JoQzhX - 8JPcARsMARm+YFRk8AL7LAoWpCVWONlwAHuy5woatJrZx12BklTAKGoz26hS9fVKE/eUiovyLfJA - W0E+2t5dje4ADTGI5hiLkvN6hqPHk4zgM2kLgVyfK5CYjr5GwMjetX1yDprZhr6WlNkJYgEoJWLN - 0atHGb2fOWOTxZaVxxzCNf78usRAu8S0lSv+Gm989NJuGK1QLAsTpWR69HkA8Lc/lvzf/k1i6pJu - lA4Yi+B0MrvombfzfEMXkyuopDa8Y93OB1eHRs6i2G0aH3fIif3ldpq0mc4X05nFGzs3g+fBPwAA - AP//AwC+wHFRRAMAAA== - headers: - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Type: - - application/json - Date: Sun, 09 Jun 2024 23:39:08 GMT - Server: DUMMY - Transfer-Encoding: - - chunked - X-Content-Type-Options: - - nosniff - access-control-expose-headers: - - X-Request-ID - status: - code: 200 - message: OK -- request: - body: '{"messages": [{"content": "You are a tour guide assistant offers information - about nearby attractions. The user is at a location and wants to know what to - learn about nearby attractions. Use the following pieces of context to suggest - nearby attractions to the user. If there are no interesting attractions nearby, - tell the user there''s nothing to see where they''re at. Use three sentences - maximum and keep your suggestions concise.\n\n---START OF CONTEXT---\nCentral - Park\n\nAmerican Museum of Natural History---END OF CONTEXT---\n\n", "role": - "system"}, {"content": "I''m at Central Park W & 79st, New York, NY 10024, United - States.", "role": "user"}], "model": "gpt-4o", "n": 1, "stream": false, "temperature": - 1.0}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - authorization: - - DUMMY - connection: - - keep-alive - content-length: - - '716' - content-type: - - application/json - host: - - api.openai.com - user-agent: - - OpenAI/Python - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - body: - string: !!binary | - H4sIAAAAAAAAA3RRsY7aQBTs+Yp326SBEzb2IeiQDumaS5GrkihCj/WzvYe9b7PvWYd14t8jGwKk - SLPFzM5o3sznBMC4wqzB2BrVtqGZbZbz7nX59vuQfMgqW8mPZLM9ZG/b7DnsyUwHBe/fyepf1aPl - NjSkjv2ZtpFQaXBNlulTusiTfD4SLRfUDLIq6CzjWTpPs9k8nyWLi7BmZ0nMGn5OAAA+x3eI6As6 - mjWMNiPSkghWZNbXTwAmcjMgBkWcKHo10xtp2Sv5MfV37r5EAucBoUSvKOosSGB9gG+uqhU8Ydz3 - 4AS0Jti0FJ1FD6+dUNcCl/AVtYvYwIsT5dhP4aOmSNBzB8NHOoaGI0GJYp1Hdb4COtZu71SAPRTO - s2AXZQoS0NIU0BfQdraGliM9wta/cz/YRSiwf7g/JFLZCQ49+q5pLvjp2kzDVYi8lwt/xUvnndS7 - SCjshxZEOZiRPU0Afo0LdP+UakLkNuhO+UB+MEzS7Oxnbpvf2EV+IZUVmztVvvqfaleQomvkbkdz - Tuh8dXOYX2OOdxrpRandlc5XFEN051nLsEvzpzRDWmBuJqfJHwAAAP//AwC6ujE63wIAAA== - headers: - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Type: - - application/json - Date: Sun, 09 Jun 2024 23:39:08 GMT - Server: DUMMY - Transfer-Encoding: - - chunked - X-Content-Type-Options: - - nosniff - access-control-expose-headers: - - X-Request-ID - status: - code: 200 - message: OK -- request: - body: '{"messages": [{"content": "Given a chat history and the latest user question - which might reference context in the chat history, formulate a standalone question - which can be understood without the chat history. Do NOT answer the question, - just reformulate it if needed and otherwise return it as is.", "role": "system"}, - {"content": "I''m at Central Park W & 79st, New York, NY 10024, United States.", - "role": "user"}, {"content": "You''re in a fantastic spot! Right nearby is the - American Museum of Natural History, where you can explore fascinating exhibits - on dinosaurs, space, and much more. Enjoy your day!", "role": "assistant"}, - {"content": "11 W 53rd St, New York, NY 10019, United States.", "role": "user"}], - "model": "gpt-4o", "n": 1, "stream": false, "temperature": 1.0}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - authorization: - - DUMMY - connection: - - keep-alive - content-length: - - '778' - content-type: - - application/json - host: - - api.openai.com - user-agent: - - OpenAI/Python - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - body: - string: !!binary | - H4sIAAAAAAAAAwAAAP//dJFBj9MwEIXv+RUjnxMUJ8126WW1QgKBoAcQqiqEokkySU0dO/JMtcCq - /x057bblsBcf3uf3/Gb8nAAo06kVqHaH0o6TzR6X+WF9/xg2X/MvxYd3n0bOP//djsumef/EKo0O - 3/yiVl5cb1o/TpbEeHfCbSAUiql6WdwVZaUrPYPRd2SjbZgkW/isyItFlleZLs/GnTctsVrBjwQA - 4Hk+Y0XX0W+1gjx9UUZixoHU6nIJQAVvo6KQ2bCgE5VeYeudkJtbb3Yo4LxgYwlQJGAb2zP4ABZd - N2LYM7To4CP0xnWAAlrDBqoydPBNUljTE2x92Kew3oLOc/02he/OCEWMQvxw+3Sg/sAYJ3cHa8/6 - 8TKL9cMUfMNnftF74wzv6kDI3sXeLH5SMz0mAD/nnR3+W4Oagh8nqcXvycVAvShPeer6S1da3J+h - eEF741rq11x1R4LG8s3m1amhccM1Ib/UnOdU/IeFxro3bqAwBXP6iH6qi+quWCCVWKnkmPwDAAD/ - /wMA9OAVapECAAA= - headers: - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Type: - - application/json - Date: Sun, 09 Jun 2024 23:39:08 GMT - Server: DUMMY - Transfer-Encoding: - - chunked - X-Content-Type-Options: - - nosniff - access-control-expose-headers: - - X-Request-ID - status: - code: 200 - message: OK -- request: - body: '{"messages": [{"content": "You are a tour guide assistant offers information - about nearby attractions. The user is at a location and wants to know what to - learn about nearby attractions. Use the following pieces of context to suggest - nearby attractions to the user. If there are no interesting attractions nearby, - tell the user there''s nothing to see where they''re at. Use three sentences - maximum and keep your suggestions concise.\n\n---START OF CONTEXT---\nCentral - Park\n\nAmerican Museum of Natural History---END OF CONTEXT---\n\n", "role": - "system"}, {"content": "I''m at Central Park W & 79st, New York, NY 10024, United - States.", "role": "user"}, {"content": "You''re in a fantastic spot! Right nearby - is the American Museum of Natural History, where you can explore fascinating - exhibits on dinosaurs, space, and much more. Enjoy your day!", "role": "assistant"}, - {"content": "11 W 53rd St, New York, NY 10019, United States.", "role": "user"}], - "model": "gpt-4o", "n": 1, "stream": false, "temperature": 1.0}' - headers: - accept: - - application/json - accept-encoding: - - gzip, deflate - authorization: - - DUMMY - connection: - - keep-alive - content-length: - - '1013' - content-type: - - application/json - host: - - api.openai.com - user-agent: - - OpenAI/Python - method: POST - uri: https://api.openai.com/v1/chat/completions - response: - body: - string: !!binary | - H4sIAAAAAAAAAwAAAP//dFGxbtswEN39FVcuaQErsCUrDrx5SwejQLeiKAyaOkmsSR7DO7lxA/97 - Qdmxk6ELh/fuPb579zoBULZRK1Cm12J8dMV6ORu+7Q+Y1st92zzrw/e/a9x1LYXon9Q0K2j3G428 - qe4N+ehQLIUzbRJqwew6X5YPZVXP63IkPDXosqyLUiyoKGflopjVxby6CHuyBlmt4OcEAOB1fHPE - 0OCLWsFs+oZ4ZNYdqtV1CEAlchlRmtmy6CBqeiMNBcEwpv5Bw11COGA6gnHECEIgPcJmYBw8UAsb - ajAFWCeBzxvarL9M4U9vTQ8tahkSMugA+CIY2B4QDDmHJleQxeNXPlLS6Qg6NODPbjrJPXyVOwYN - fmApDpatQEspU4BB+oGtZuFP75MnbAfWubgwOHfBT9cqHHUx0Y4v/BVvbbDcbxNqppDXZqGoRvY0 - Afg1Vj58aFHFRD7KVmiPIRvOH+uzn7od+cZW1YUUEu1ueDl//J9q26Bo6/jd4dQ5oQ3dzWF2jTnu - qfjIgn7b2tBhisme79jGbVk/lAuNla7V5DT5BwAA//8DAE9G6b7QAgAA - headers: - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Type: - - application/json - Date: Sun, 09 Jun 2024 23:39:08 GMT - Server: DUMMY - Transfer-Encoding: - - chunked - X-Content-Type-Options: - - nosniff - access-control-expose-headers: - - X-Request-ID - status: - code: 200 - message: OK - request: body: '{"messages": [{"content": "You are a tour guide assistant offers information about nearby attractions. The user is at a location and wants to know what to @@ -464,17 +32,18 @@ interactions: method: POST uri: https://api.openai.com/v1/chat/completions response: - content: "{\n \"id\": \"chatcmpl-ABSmTDemjarbXfNZaY8RJh7UocVAi\",\n \"object\": - \"chat.completion\",\n \"created\": 1727295605,\n \"model\": \"gpt-4o-2024-05-13\",\n + content: "{\n \"id\": \"chatcmpl-AEgzDhivTNpFojxM1BuqTCzQRnzye\",\n \"object\": + \"chat.completion\",\n \"created\": 1728065195,\n \"model\": \"gpt-4o-2024-08-06\",\n \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": - \"assistant\",\n \"content\": \"You're in a great spot! Just a short - walk away is the American Museum of Natural History, where you can explore fascinating - exhibits on science, culture, and natural history. Inside Central Park itself, - don't miss out on the scenic Belvedere Castle and the tranquil Shakespeare Garden.\",\n - \ \"refusal\": null\n },\n \"logprobs\": null,\n \"finish_reason\": - \"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\": 124,\n \"completion_tokens\": - 55,\n \"total_tokens\": 179,\n \"completion_tokens_details\": {\n \"reasoning_tokens\": - 0\n }\n },\n \"system_fingerprint\": \"fp_e375328146\"\n}\n" + \"assistant\",\n \"content\": \"You're right by the American Museum of + Natural History, a fascinating place with exhibits on everything from dinosaurs + to space. Enjoy a stroll through Central Park, which offers beautiful landscapes, + walking paths, and iconic spots like Bow Bridge and Bethesda Terrace.\",\n \"refusal\": + null\n },\n \"logprobs\": null,\n \"finish_reason\": \"stop\"\n + \ }\n ],\n \"usage\": {\n \"prompt_tokens\": 124,\n \"completion_tokens\": + 47,\n \"total_tokens\": 171,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0\n },\n \"completion_tokens_details\": {\n \"reasoning_tokens\": + 0\n }\n },\n \"system_fingerprint\": \"fp_2f406b9113\"\n}\n" headers: Connection: - keep-alive @@ -498,13 +67,13 @@ interactions: which can be understood without the chat history. Do NOT answer the question, just reformulate it if needed and otherwise return it as is.", "role": "system"}, {"content": "I''m at Central Park W & 79st, New York, NY 10024, United States.", - "role": "user"}, {"content": "You''re in a great spot! Just a short walk away - is the American Museum of Natural History, where you can explore fascinating - exhibits on science, culture, and natural history. Inside Central Park itself, - don''t miss out on the scenic Belvedere Castle and the tranquil Shakespeare - Garden.", "role": "assistant"}, {"content": "11 W 53rd St, New York, NY 10019, - United States.", "role": "user"}], "model": "gpt-4o", "n": 1, "stream": false, - "temperature": 1.0}' + "role": "user"}, {"content": "You''re right by the American Museum of Natural + History, a fascinating place with exhibits on everything from dinosaurs to space. + Enjoy a stroll through Central Park, which offers beautiful landscapes, walking + paths, and iconic spots like Bow Bridge and Bethesda Terrace.", "role": "assistant"}, + {"content": "content=''11 W 53rd St, New York, NY 10019, United States.'' additional_kwargs={} + response_metadata={} id=''11''", "role": "user"}], "model": "gpt-4o", "n": 1, + "stream": false, "temperature": 1.0}' headers: accept: - application/json @@ -515,7 +84,7 @@ interactions: connection: - keep-alive content-length: - - '885' + - '930' content-type: - application/json host: @@ -527,15 +96,16 @@ interactions: method: POST uri: https://api.openai.com/v1/chat/completions response: - content: "{\n \"id\": \"chatcmpl-ABSmWw8H4W1sT6HSuTjHSeSer9518\",\n \"object\": - \"chat.completion\",\n \"created\": 1727295608,\n \"model\": \"gpt-4o-2024-05-13\",\n + content: "{\n \"id\": \"chatcmpl-AEgzFbJHJ9XNvxQPetmr4PZNphaYq\",\n \"object\": + \"chat.completion\",\n \"created\": 1728065197,\n \"model\": \"gpt-4o-2024-08-06\",\n \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": - \"assistant\",\n \"content\": \"What nearby attractions or points of - interest are located near 11 W 53rd St, New York, NY 10019, United States?\",\n - \ \"refusal\": null\n },\n \"logprobs\": null,\n \"finish_reason\": - \"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\": 163,\n \"completion_tokens\": - 29,\n \"total_tokens\": 192,\n \"completion_tokens_details\": {\n \"reasoning_tokens\": - 0\n }\n },\n \"system_fingerprint\": \"fp_52a7f40b0b\"\n}\n" + \"assistant\",\n \"content\": \"I see you're mentioning a new location. + Are you planning to visit the Museum of Modern Art at this address?\",\n \"refusal\": + null\n },\n \"logprobs\": null,\n \"finish_reason\": \"stop\"\n + \ }\n ],\n \"usage\": {\n \"prompt_tokens\": 167,\n \"completion_tokens\": + 22,\n \"total_tokens\": 189,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0\n },\n \"completion_tokens_details\": {\n \"reasoning_tokens\": + 0\n }\n },\n \"system_fingerprint\": \"fp_e5e4913e83\"\n}\n" headers: Connection: - keep-alive @@ -562,13 +132,13 @@ interactions: maximum and keep your suggestions concise.\n\n---START OF CONTEXT---\nCentral Park\n\nAmerican Museum of Natural History---END OF CONTEXT---\n\n", "role": "system"}, {"content": "I''m at Central Park W & 79st, New York, NY 10024, United - States.", "role": "user"}, {"content": "You''re in a great spot! Just a short - walk away is the American Museum of Natural History, where you can explore fascinating - exhibits on science, culture, and natural history. Inside Central Park itself, - don''t miss out on the scenic Belvedere Castle and the tranquil Shakespeare - Garden.", "role": "assistant"}, {"content": "11 W 53rd St, New York, NY 10019, - United States.", "role": "user"}], "model": "gpt-4o", "n": 1, "stream": false, - "temperature": 1.0}' + States.", "role": "user"}, {"content": "You''re right by the American Museum + of Natural History, a fascinating place with exhibits on everything from dinosaurs + to space. Enjoy a stroll through Central Park, which offers beautiful landscapes, + walking paths, and iconic spots like Bow Bridge and Bethesda Terrace.", "role": + "assistant"}, {"content": "11 W 53rd St, New York, NY 10019, United States.", + "role": "user"}], "model": "gpt-4o", "n": 1, "stream": false, "temperature": + 1.0}' headers: accept: - application/json @@ -579,7 +149,7 @@ interactions: connection: - keep-alive content-length: - - '1120' + - '1105' content-type: - application/json host: @@ -591,16 +161,18 @@ interactions: method: POST uri: https://api.openai.com/v1/chat/completions response: - content: "{\n \"id\": \"chatcmpl-ABSmWUQj2eV0sYS4C3NNXW0PRqcpF\",\n \"object\": - \"chat.completion\",\n \"created\": 1727295608,\n \"model\": \"gpt-4o-2024-05-13\",\n + content: "{\n \"id\": \"chatcmpl-AEgzGbPWcNf9UC78PouyK4Jdo0KoM\",\n \"object\": + \"chat.completion\",\n \"created\": 1728065198,\n \"model\": \"gpt-4o-2024-08-06\",\n \ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": - \"assistant\",\n \"content\": \"You're right at the Museum of Modern - Art (MoMA), a premier spot for contemporary and modern art. Just a few blocks - away, you can also visit Rockefeller Center with its famous observation deck, - Top of the Rock.\",\n \"refusal\": null\n },\n \"logprobs\": - null,\n \"finish_reason\": \"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\": - 205,\n \"completion_tokens\": 44,\n \"total_tokens\": 249,\n \"completion_tokens_details\": - {\n \"reasoning_tokens\": 0\n }\n },\n \"system_fingerprint\": \"fp_e375328146\"\n}\n" + \"assistant\",\n \"content\": \"You're near the Museum of Modern Art + (MoMA), home to an impressive collection of modern and contemporary artworks. + Just a short walk away, you'll find Rockefeller Center, offering shopping, dining, + and an observation deck with views of the city.\",\n \"refusal\": null\n + \ },\n \"logprobs\": null,\n \"finish_reason\": \"stop\"\n }\n + \ ],\n \"usage\": {\n \"prompt_tokens\": 197,\n \"completion_tokens\": + 48,\n \"total_tokens\": 245,\n \"prompt_tokens_details\": {\n \"cached_tokens\": + 0\n },\n \"completion_tokens_details\": {\n \"reasoning_tokens\": + 0\n }\n },\n \"system_fingerprint\": \"fp_2f406b9113\"\n}\n" headers: Connection: - keep-alive diff --git a/tests/test_helpers/test_assistants.py b/tests/test_helpers/test_assistants.py index 940ed5c..5cb0bb0 100644 --- a/tests/test_helpers/test_assistants.py +++ b/tests/test_helpers/test_assistants.py @@ -3,7 +3,12 @@ import pytest from langchain_core.documents import Document -from langchain_core.messages import AIMessage, HumanMessage, messages_to_dict +from langchain_core.messages import ( + AIMessage, + HumanMessage, + ToolMessage, + messages_to_dict, +) from langchain_core.retrievers import BaseRetriever from django_ai_assistant.helpers.assistants import AIAssistant @@ -76,54 +81,83 @@ def test_AIAssistant_invoke(): thread = Thread.objects.create(name="Recife Temperature Chat") assistant = AIAssistant.get_cls("temperature_assistant")() - response_0 = assistant.invoke( + assistant.invoke( {"input": "What is the temperature today in Recife?"}, thread_id=thread.id, ) - response_1 = assistant.invoke( + response = assistant.invoke( {"input": "What about tomorrow?"}, thread_id=thread.id, ) - - messages = thread.messages.order_by("created_at").values_list("message", flat=True) - messages_ids = thread.messages.order_by("created_at").values_list("id", flat=True) - - assert response_0["input"] == "What is the temperature today in Recife?" - assert response_0["output"] == "The current temperature today in Recife is 32 degrees Celsius." - assert response_1["input"] == "What about tomorrow?" - assert ( - response_1["output"] - == "The forecasted temperature in Recife for tomorrow, June 10th, is 35 degrees Celsius." - ) - - question_message = response_1["messages"][1] - assert question_message.content == "What is the temperature today in Recife?" - assert question_message.id == str(messages_ids[0]) - response_message = response_1["messages"][2] - assert ( - response_message.content == "The current temperature today in Recife is 32 degrees Celsius." - ) - assert response_message.id == str(messages_ids[1]) + response_messages = messages_to_dict(response["messages"]) + stored_messages = messages_to_dict(thread.get_messages(include_extra_messages=True)) expected_messages = messages_to_dict( [ - HumanMessage(content="What is the temperature today in Recife?", id=messages_ids[0]), + HumanMessage( + content="What is the temperature today in Recife?", + id="1", + ), + AIMessage( + content="", + id="2", + tool_calls=[ + { + "name": "fetch_current_temperature", + "args": {"location": "Recife"}, + "id": "call_mp680g1ciZRb9eaRoWZUpMWG", + "type": "tool_call", + } + ], + ), + ToolMessage( + content="32 degrees Celsius", + name="fetch_current_temperature", + id="3", + tool_call_id="call_mp680g1ciZRb9eaRoWZUpMWG", + ), AIMessage( - content="The current temperature today in Recife is 32 degrees Celsius.", - id=messages_ids[1], + content="The current temperature in Recife is 32 degrees Celsius.", + id="4", + ), + HumanMessage( + content="What about tomorrow?", additional_kwargs={}, response_metadata={}, id="5" ), - HumanMessage(content="What about tomorrow?", id=messages_ids[2]), AIMessage( - content="The forecasted temperature in Recife for tomorrow, June 10th, is 35 degrees Celsius.", - id=messages_ids[3], + content="", + id="6", + tool_calls=[ + { + "name": "fetch_forecast_temperature", + "args": {"location": "Recife", "dt_str": "2024-06-10"}, + "id": "call_5Y5P4y5m0VFzh5GE0RNeEkyP", + "type": "tool_call", + } + ], + ), + ToolMessage( + content="35 degrees Celsius", + name="fetch_forecast_temperature", + id="7", + tool_call_id="call_5Y5P4y5m0VFzh5GE0RNeEkyP", + ), + AIMessage( + content="The forecasted temperature for tomorrow in Recife is 35 degrees Celsius.", + id="8", ), ] ) - - assert [m["data"]["content"] for m in list(messages)] == [ - m["data"]["content"] for m in expected_messages - ] - assert [m["data"]["id"] for m in list(messages)] == [m["data"]["id"] for m in expected_messages] + assert response_messages[0]["data"]["type"] == "system" + assert ( + response_messages[0]["data"]["content"] == "You are a temperature bot. Today is 2024-06-09." + ) + for attr in ["id", "content", "type", "tool_calls", "tool_call_id"]: + assert [m["data"].get(attr) for m in list(response_messages[1:])] == [ + m["data"].get(attr) for m in expected_messages + ] + assert [m["data"].get(attr) for m in list(stored_messages)] == [ + m["data"].get(attr) for m in expected_messages + ] def test_AIAssistant_run_handles_optional_thread_id_param(): @@ -172,14 +206,17 @@ def test_AIAssistant_with_rag_invoke(): assert response_0["input"] == "I'm at Central Park W & 79st, New York, NY 10024, United States." assert response_0["output"] == ( - "You're in a great spot! Just a short walk away is the American Museum of Natural History, " - "where you can explore fascinating exhibits on science, culture, and natural history. " - "Inside Central Park itself, don't miss out on the scenic Belvedere Castle and the tranquil Shakespeare Garden." + "You're right by the American Museum of Natural History, " + "a fascinating place with exhibits on everything from dinosaurs to space. " + "Enjoy a stroll through Central Park, which offers beautiful landscapes, " + "walking paths, and iconic spots like Bow Bridge and Bethesda Terrace." ) assert response_1["input"] == "11 W 53rd St, New York, NY 10019, United States." assert response_1["output"] == ( - "You're right at the Museum of Modern Art (MoMA), a premier spot for contemporary and modern art. " - "Just a few blocks away, you can also visit Rockefeller Center with its famous observation deck, Top of the Rock." + "You're near the Museum of Modern Art (MoMA), " + "home to an impressive collection of modern and contemporary artworks. " + "Just a short walk away, you'll find Rockefeller Center, offering shopping, " + "dining, and an observation deck with views of the city." ) expected_messages = messages_to_dict( diff --git a/tests/test_views.py b/tests/test_views.py index f3e78cc..e5498a9 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -9,7 +9,7 @@ from django_ai_assistant import PACKAGE_NAME, VERSION from django_ai_assistant.helpers.assistants import AIAssistant -from django_ai_assistant.langchain.chat_message_histories import DjangoChatMessageHistory +from django_ai_assistant.helpers.django_messages import save_django_messages from django_ai_assistant.langchain.tools import BaseModel, Field, method_tool from django_ai_assistant.models import Message, Thread @@ -286,7 +286,7 @@ def test_cannot_delete_thread_if_unauthorized(): @pytest.mark.django_db(transaction=True) def test_list_thread_messages(authenticated_client): thread = baker.make(Thread, created_by=User.objects.first()) - DjangoChatMessageHistory(thread.id).add_messages([HumanMessage(content="Hello")]) + save_django_messages([HumanMessage(content="Hello")], thread=thread) response = authenticated_client.get( reverse("django_ai_assistant:messages_list_create", kwargs={"thread_id": thread.id}) ) @@ -298,7 +298,7 @@ def test_list_thread_messages(authenticated_client): @pytest.mark.django_db(transaction=True) def test_does_not_list_thread_messages_if_not_thread_user(authenticated_client): thread = baker.make(Thread, created_by=baker.make(User)) - DjangoChatMessageHistory(thread.id).add_messages([HumanMessage(content="Hello")]) + save_django_messages([HumanMessage(content="Hello")], thread=thread) response = authenticated_client.get( reverse("django_ai_assistant:messages_list_create", kwargs={"thread_id": thread.id}) ) @@ -323,18 +323,15 @@ def test_create_thread_message(authenticated_client): ) assert response.status_code == HTTPStatus.CREATED - assert Message.objects.count() == 2 + chat_messages = thread.get_messages(include_extra_messages=False) + assert len(chat_messages) == 2 - human_message = Message.objects.filter(message__type="human").first() - ai_message = Message.objects.filter(message__type="ai").first() + human_message = chat_messages[0] + ai_message = chat_messages[1] + assert human_message.content == "Hello, what is the temperature in SF right now?" assert ( - human_message.message["data"]["content"] - == "Hello, what is the temperature in SF right now?" - ) - assert ( - ai_message.message["data"]["content"] - == "The current temperature in San Francisco, CA is 32 degrees Celsius." + ai_message.content == "The current temperature in San Francisco, CA is 32 degrees Celsius." ) From c350737e833a824f44f84be272de34da0460dc6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=A1vio=20Juvenal?= Date: Fri, 4 Oct 2024 17:12:38 -0300 Subject: [PATCH 2/2] Improve coverage --- .coveragerc | 7 +++++ tests/test_conf.py | 63 +++++++++++++++++++++++++++++++++++++++++++++ tests/test_views.py | 10 +++++++ 3 files changed, 80 insertions(+) create mode 100644 tests/test_conf.py diff --git a/.coveragerc b/.coveragerc index d1f6fb5..264abff 100644 --- a/.coveragerc +++ b/.coveragerc @@ -8,3 +8,10 @@ omit = */migrations/* *static* */templates/* + management/commands/generate_openapi_schema.py + apps.py + admin.py +exclude_lines = + pragma: no cover + if TYPE_CHECKING: + raise NotImplementedError diff --git a/tests/test_conf.py b/tests/test_conf.py new file mode 100644 index 0000000..afb2e00 --- /dev/null +++ b/tests/test_conf.py @@ -0,0 +1,63 @@ +from django.core.exceptions import ImproperlyConfigured +from django.test import override_settings + +import pytest + +from django_ai_assistant.conf import DEFAULTS, PREFIX, Settings + + +@pytest.fixture +def settings(): + return Settings() + + +def test_getattr_existing_setting(settings): + assert settings.INIT_API_FN == DEFAULTS["INIT_API_FN"] + + +def test_getattr_non_existing_setting(settings): + with pytest.raises(AttributeError): + settings.NON_EXISTING_SETTING # noqa: B018 + + +def test_get_setting_default(settings): + assert settings.get_setting("INIT_API_FN") == DEFAULTS["INIT_API_FN"] + + +@override_settings(AI_ASSISTANT_INIT_API_FN="custom.module.init_api") +def test_get_setting_override(settings): + assert settings.get_setting("INIT_API_FN") == "custom.module.init_api" + + +def test_change_setting_enter(settings): + settings.change_setting(f"{PREFIX}INIT_API_FN", "new.value", enter=True) + assert settings.INIT_API_FN == "new.value" + + +def test_change_setting_exit(settings): + settings.change_setting(f"{PREFIX}INIT_API_FN", "new.value", enter=True) + assert settings.INIT_API_FN == "new.value" + settings.change_setting(f"{PREFIX}INIT_API_FN", None, enter=False) + assert settings.INIT_API_FN == DEFAULTS["INIT_API_FN"] + + +def test_change_setting_invalid_prefix(settings): + settings.change_setting("INVALID_PREFIX_SETTING", "value", enter=True) + assert not hasattr(settings, "SETTING") + + +def test_change_setting_invalid_setting(settings): + settings.change_setting(f"{PREFIX}INVALID_SETTING", "value", enter=True) + assert not hasattr(settings, "INVALID_SETTING") + + +def test_call_fn(settings): + result = settings.call_fn("INIT_API_FN") + assert result + + +@override_settings(AI_ASSISTANT_INIT_API_FN="django.core.exceptions.ImproperlyConfigured") +def test_call_fn_override(settings): + result = settings.call_fn("INIT_API_FN") + + assert isinstance(result, ImproperlyConfigured) diff --git a/tests/test_views.py b/tests/test_views.py index e5498a9..f6c8a06 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -172,6 +172,16 @@ def test_gets_specific_thread(authenticated_client): assert response.json()["id"] == thread.id +@pytest.mark.django_db(transaction=True) +def test_gets_specific_thread_that_does_not_exist(authenticated_client): + response = authenticated_client.get( + reverse("django_ai_assistant:thread_detail_update_delete", kwargs={"thread_id": 1000000}) + ) + + assert response.status_code == HTTPStatus.NOT_FOUND + assert response.json() == {"detail": "Not Found"} + + def test_does_not_list_threads_if_unauthorized(): # TODO: Implement this test once permissions are in place pass