Skip to content

Commit 0756cd7

Browse files
committed
RDBC-1059 Add AiAgentActionRequest.type and sub_conversation_id
1 parent ff7bd17 commit 0756cd7

3 files changed

Lines changed: 47 additions & 27 deletions

File tree

ravendb/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
RunConversationOperation,
102102
ConversationResult,
103103
AiAgentActionRequest,
104+
AiAgentActionRequestType,
104105
AiAgentActionResponse,
105106
AiAgentArtificialActionResponse,
106107
AiUsage,

ravendb/documents/operations/ai/agents/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
RunConversationOperation,
2828
ConversationResult,
2929
AiAgentActionRequest,
30+
AiAgentActionRequestType,
3031
AiAgentActionResponse,
3132
AiAgentArtificialActionResponse,
3233
AiUsage,
@@ -48,6 +49,7 @@
4849
"RunConversationOperation",
4950
"ConversationResult",
5051
"AiAgentActionRequest",
52+
"AiAgentActionRequestType",
5153
"AiAgentActionResponse",
5254
"AiAgentArtificialActionResponse",
5355
"AiUsage",

ravendb/documents/operations/ai/agents/run_conversation_operation.py

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from __future__ import annotations
2+
import enum
23
import json
34
from dataclasses import dataclass
45
from typing import Optional, List, Dict, Any, TypeVar, Generic, Callable
@@ -14,34 +15,68 @@
1415
TSchema = TypeVar("TSchema")
1516

1617

17-
class AiAgentActionRequest:
18-
"""Represents an action request from an AI agent."""
18+
class AiAgentActionRequestType(enum.Enum):
19+
USER_ACTION = "UserAction"
20+
SUB_AGENT = "SubAgent"
21+
22+
def __str__(self) -> str:
23+
return self.value
24+
1925

20-
def __init__(self, name: str = None, tool_id: str = None, arguments: str = None):
26+
class AiAgentActionRequest:
27+
def __init__(
28+
self,
29+
name: str = None,
30+
tool_id: str = None,
31+
arguments: str = None,
32+
type: AiAgentActionRequestType = AiAgentActionRequestType.USER_ACTION,
33+
sub_conversation_id: Optional[str] = None,
34+
):
2135
self.name = name
2236
self.tool_id = tool_id
2337
self.arguments = arguments
38+
self.type = type
39+
self.sub_conversation_id = sub_conversation_id
2440

2541
@classmethod
2642
def from_json(cls, json_dict: Dict[str, Any]) -> AiAgentActionRequest:
2743
return cls(
2844
name=json_dict.get("Name"),
2945
tool_id=json_dict.get("ToolId"),
3046
arguments=json_dict.get("Arguments"),
47+
type=AiAgentActionRequestType(json_dict.get("Type") or "UserAction"),
48+
sub_conversation_id=json_dict.get("SubConversationId"),
3149
)
3250

3351
def to_json(self) -> Dict[str, Any]:
3452
return {
3553
"Name": self.name,
3654
"ToolId": self.tool_id,
3755
"Arguments": self.arguments,
56+
"Type": self.type.value,
57+
"SubConversationId": self.sub_conversation_id,
3858
}
3959

60+
def __eq__(self, other: object) -> bool:
61+
if not isinstance(other, AiAgentActionRequest):
62+
return False
63+
return (
64+
self.tool_id == other.tool_id
65+
and self.name == other.name
66+
and self.arguments == other.arguments
67+
and self.type == other.type
68+
and self.sub_conversation_id == other.sub_conversation_id
69+
)
70+
71+
def __hash__(self) -> int:
72+
return hash((self.tool_id, self.name, self.arguments, self.type, self.sub_conversation_id))
73+
74+
def __repr__(self) -> str:
75+
return json.dumps(self.to_json())
76+
4077

4178
@dataclass
4279
class AiAgentActionResponse:
43-
"""Represents a response to an AI agent action request."""
44-
4580
tool_id: Optional[str] = None
4681
content: Optional[str] = None
4782

@@ -58,16 +93,12 @@ def to_json(self) -> Dict[str, Any]:
5893

5994
@dataclass
6095
class AiAgentArtificialActionResponse:
61-
"""
62-
Represents an artificial action (tool call) and response to inject into the model's conversation context.
63-
This allows programmatically prompting the agent by making it "believe" it executed a tool.
64-
"""
65-
96+
# Synthetic (tool_id, content) pair injected to make the model "believe"
97+
# it executed a tool. Sent in addition to a real ActionResponses entry.
6698
tool_id: Optional[str] = None
6799
content: Optional[str] = None
68100

69101
def validate(self) -> None:
70-
"""Validates that tool_id and content are not empty."""
71102
if not self.tool_id or self.tool_id.isspace():
72103
raise ValueError("tool_id cannot be None or empty")
73104
if not self.content or self.content.isspace():
@@ -86,8 +117,6 @@ def to_json(self) -> Dict[str, Any]:
86117

87118
@dataclass
88119
class AiUsage:
89-
"""Represents AI token usage statistics."""
90-
91120
prompt_tokens: int = 0
92121
completion_tokens: int = 0
93122
total_tokens: int = 0
@@ -115,27 +144,15 @@ def to_json(self) -> Dict[str, Any]:
115144

116145
@staticmethod
117146
def get_usage_difference(current: AiUsage, previous: AiUsage) -> AiUsage:
118-
"""
119-
Calculate the usage difference between current and previous usage.
120-
121-
Args:
122-
current: The current usage statistics
123-
previous: The previous usage statistics
124-
125-
Returns:
126-
An AiUsage object representing the difference
127-
"""
147+
# cached/completion/reasoning are last-response-only, so they pass
148+
# through. prompt/total are clamped against bogus negative model output.
128149
previous_total_without_reasoning = (
129150
previous.completion_tokens - previous.reasoning_tokens + previous.prompt_tokens
130151
)
131152
return AiUsage(
132-
# in case the model gives us crappy results and current.prompt_tokens - previous_total_without_reasoning < 0
133153
prompt_tokens=max(current.prompt_tokens - previous_total_without_reasoning, 0),
134-
# in case the model gives us crappy results and current.total_tokens - previous_total_without_reasoning < 0
135154
total_tokens=max(current.total_tokens - previous_total_without_reasoning, 0),
136-
# we don't want to subtract cached tokens, as they are only for the last response
137155
cached_tokens=current.cached_tokens,
138-
# we don't want to subtract completion tokens, as they are only for the last response
139156
completion_tokens=current.completion_tokens,
140157
reasoning_tokens=current.reasoning_tokens,
141158
)

0 commit comments

Comments
 (0)