Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions cognite/client/_api/agents/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ def chat(
Users can ensure conversation continuity by including the cursor from the previous response in subsequent requests.

Args:
agent_id (str): External ID that uniquely identifies the agent.
agent_id (str): External ID that uniquely identifies the agent. Deprecated: This parameter will be renamed in a future release. The SDK now sends this value as `agentExternalId` to the API; server-side `agentId` is deprecated.
messages (Message | Sequence[Message]): A list of one or many input messages to the agent.
cursor (str | None): The cursor to use for continuation of a conversation. Use this to
create multi-turn conversations, as the cursor will keep track of the conversation state.
Expand Down Expand Up @@ -299,7 +299,7 @@ def chat(

# Build request body
body = {
"agentId": agent_id,
"agentExternalId": agent_id,
"messages": MessageList(messages).dump(camel_case=True),
}

Expand Down
3 changes: 3 additions & 0 deletions cognite/client/data_classes/agents/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class AgentCore(WriteableCogniteResource["AgentUpsert"]):

Args:
external_id (str): The external ID provided by the client. Must be unique for the resource type.
This value is used as ``agentExternalId`` when interacting with the chat API.
name (str): The name of the agent.
description (str | None): The description of the agent.
instructions (str | None): Instructions for the agent.
Expand All @@ -46,6 +47,7 @@ class AgentUpsert(AgentCore):

Args:
external_id (str): The external ID provided by the client. Must be unique for the resource type.
This value is used as ``agentExternalId`` when interacting with the chat API.
name (str): The name of the agent, for use in user interfaces.
description (str | None): The human readable description of the agent.
instructions (str | None): Instructions for the agent.
Expand Down Expand Up @@ -112,6 +114,7 @@ class Agent(AgentCore):

Args:
external_id (str): The external ID provided by the client. Must be unique for the resource type.
This value is used as ``agentExternalId`` when interacting with the chat API.
name (str): The name of the agent, for use in user interfaces.
description (str | None): The human readable description of the agent.
instructions (str | None): Instructions for the agent.
Expand Down
14 changes: 11 additions & 3 deletions cognite/client/data_classes/agents/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,11 @@ class AgentChatResponse(CogniteResource):
"""Response from agent chat.

Args:
agent_id (str): The ID of the agent.
agent_id (str): The ID of the agent. Deprecated: Will be removed in a future release in favor of `agent_external_id`.
messages (AgentMessageList): The response messages from the agent.
type (str): The response type.
cursor (str | None): Cursor for conversation continuation.
agent_external_id (str | None): The external ID of the agent. Prefer this over `agent_id`.
"""

def __init__(
Expand All @@ -230,15 +231,18 @@ def __init__(
messages: AgentMessageList,
type: str,
cursor: str | None = None,
agent_external_id: str | None = None,
) -> None:
self.agent_id = agent_id
self.agent_external_id = agent_external_id or agent_id
self.cursor = cursor
self.messages = messages
self.type = type

def dump(self, camel_case: bool = True) -> dict[str, Any]:
result = {
"agentId" if camel_case else "agent_id": self.agent_id,
("agentExternalId" if camel_case else "agent_external_id"): self.agent_external_id,
("agentId" if camel_case else "agent_id"): self.agent_id,
"response": {
"cursor": self.cursor,
"messages": [msg.dump(camel_case=camel_case) for msg in self.messages] if self.messages else [],
Expand All @@ -263,11 +267,15 @@ def _load(cls, data: dict[str, Any], cognite_client: CogniteClient | None = None
messages = [AgentMessage._load(msg, cognite_client) for msg in raw_messages]
messages_list = AgentMessageList(messages)

agent_external_id = data.get("agentExternalId")
agent_id = data.get("agentId") or agent_external_id

instance = cls(
agent_id=data["agentId"],
agent_id=agent_id,
cursor=response_data.get("cursor"),
messages=messages_list,
type=response_data["type"],
agent_external_id=agent_external_id,
)

return instance
4 changes: 4 additions & 0 deletions docs/source/agents.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ Chat with an agent by external id
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. automethod:: cognite.client._api.agents.agents.AgentsAPI.chat

.. warning::
The server-side field ``agentId`` is deprecated. The SDK sends ``agentExternalId``
to the API and will remove references to ``agentId`` in a future release.

Agent Data classes
^^^^^^^^^^^^^^^^^^
.. automodule:: cognite.client.data_classes.agents
Expand Down
29 changes: 28 additions & 1 deletion tests/tests_unit/test_api/test_agents_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
def chat_response_body() -> dict:
return {
"agentId": "my_agent",
"agentExternalId": "my_agent",
"response": {
"cursor": "cursor_12345",
"messages": [
Expand Down Expand Up @@ -68,7 +69,8 @@ def test_chat_simple_message(self, cognite_client: CogniteClient, chat_response_
cognite_client.agents._post.assert_called_once()
call_args = cognite_client.agents._post.call_args
assert call_args[1]["url_path"] == "/ai/agents/chat"
assert call_args[1]["json"]["agentId"] == "my_agent"
assert call_args[1]["json"]["agentExternalId"] == "my_agent"
assert "agentId" not in call_args[1]["json"]
assert len(call_args[1]["json"]["messages"]) == 1
assert call_args[1]["json"]["messages"][0]["content"]["text"] == "What can you help me with?"
assert call_args[1]["json"]["messages"][0]["content"]["type"] == "text"
Expand Down Expand Up @@ -138,6 +140,7 @@ def test_chat_response_without_optional_fields(self, cognite_client: CogniteClie
# Minimal response without data or reasoning
minimal_response = {
"agentId": "my_agent",
"agentExternalId": "my_agent",
"response": {
"cursor": None,
"messages": [
Expand All @@ -162,6 +165,30 @@ def test_chat_response_without_optional_fields(self, cognite_client: CogniteClie
assert response.messages[0].reasoning is None
assert response.text == "Simple response"

def test_chat_response_with_only_agent_external_id(self, cognite_client: CogniteClient) -> None:
# Response includes only agentExternalId, not agentId
response_body = {
"agentExternalId": "my_agent",
"response": {
"cursor": None,
"messages": [
{
"content": {"text": "Hello", "type": "text"},
"role": "agent",
}
],
"type": "result",
},
}

cognite_client.agents._post = MagicMock(return_value=MagicMock(json=lambda: response_body))

response = cognite_client.agents.chat(agent_id="my_agent", messages=Message("Hi"))

assert isinstance(response, AgentChatResponse)
assert response.agent_id == "my_agent"
assert getattr(response, "agent_external_id") == "my_agent"

def test_message_creation_from_string(self) -> None:
# Test that string is automatically converted to TextContent
msg = Message("Hello world")
Expand Down
Loading