Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
38bdd3f
Target 7.2.5 and mirror the new server constants
poissoncorp Sep 8, 2026
e6d98d0
Let S3 settings turn off checksum validation
poissoncorp Sep 8, 2026
03f97a3
Add the Azure Service Bus queue broker
poissoncorp Sep 8, 2026
3a3fc94
Let a conversation cancel the tool calls it is still waiting on
poissoncorp Sep 8, 2026
5c4df30
Read an AI agent conversation back with GetConversationMessagesOperation
poissoncorp Sep 8, 2026
1253a7f
Carry SSO configuration on client certificates
poissoncorp Sep 8, 2026
a00307a
Add the CDC Sink client surface
poissoncorp Sep 8, 2026
708afa7
Manage server-wide connection strings and report what uses them
poissoncorp Sep 8, 2026
16450bd
Export the new 7.2.5 types from the package root
poissoncorp Sep 8, 2026
edc55a7
Drop an unused import from the conversation-messages tests
poissoncorp Sep 8, 2026
5ca9534
Add the Queue Sink client surface
poissoncorp Sep 8, 2026
82299ad
Add the database smuggler, reachable as store.smuggler
poissoncorp Sep 8, 2026
ab2c14a
Carry queue sinks on the database record and harden its deserialization
poissoncorp Sep 8, 2026
fc0de65
Export the queue sink and smuggler types from the package root
poissoncorp Sep 8, 2026
25e137a
Gate the licensed queue sink tests behind RAVENDB_LICENSE
poissoncorp Sep 8, 2026
3e444fe
Type license limits with LimitType and LicenseLimitException
poissoncorp Sep 10, 2026
c4fe849
Stop AutoIndexDefinition.from_json requiring optional sections
poissoncorp Sep 10, 2026
bd9b6e4
Convert between backup and remote-attachment cloud settings
poissoncorp Sep 10, 2026
ca3c520
Give smuggler operations a typed result
poissoncorp Sep 10, 2026
d7b8d11
Add CDC Sink schema discovery and mapping preview
poissoncorp Sep 10, 2026
7ddb0ec
Exercise server-wide connection strings against a licensed server
poissoncorp Sep 10, 2026
58afe65
Export the follow-up types from the package root
poissoncorp Sep 10, 2026
58ba64a
Assert what a completed operation reports, not that it reports nothing
poissoncorp Sep 10, 2026
cfa81f1
Track the 7.2.6 client version and its CDC slot-naming note
poissoncorp Sep 10, 2026
d0c6d12
Let a conversation turn override the agent's output schema
poissoncorp Sep 10, 2026
519a9a6
Let embeddings generation store each chunk's text
poissoncorp Sep 10, 2026
a5ede50
Type a failed agent query tool as QueryToolFailedException
poissoncorp Sep 10, 2026
f89590f
Keep a vector search intact when a query alias is applied
poissoncorp Sep 10, 2026
6b8b384
Keep every new parameter additive and leave wait_for_completion alone
poissoncorp Sep 11, 2026
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
103 changes: 93 additions & 10 deletions ravendb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,11 @@
)
from ravendb.documents.operations.configuration.definitions import StudioConfiguration, StudioEnvironment

from ravendb.documents.operations.connection_strings import ConnectionString
from ravendb.documents.operations.connection_strings import (
ConnectionString,
ConnectionStringUsage,
ConnectionStringUsageKind,
)

# AI Operations
from ravendb.documents.ai import (
Expand Down Expand Up @@ -115,6 +119,13 @@
GetAiAgentsResponse,
AddOrUpdateAiAgentOperation,
DeleteAiAgentOperation,
GetConversationMessagesOperation,
GetConversationMessagesOptions,
AiConversationDetailLevel,
AiConversationMessage,
AiConversationMessagesResult,
AiMessageRole,
AiToolCallResult,
)
from ravendb.documents.operations.ai import (
ChunkingOptions,
Expand Down Expand Up @@ -188,6 +199,74 @@
from ravendb.documents.operations.ongoing_tasks import (
OngoingTaskPullReplicationAsSink,
OngoingTaskPullReplicationAsHub,
OngoingTaskCdcSink,
OngoingTaskQueueSink,
)
from ravendb.documents.operations.queue_sink import (
AddQueueSinkOperation,
AddQueueSinkOperationResult,
AzureServiceBusSinkSource,
QueueSinkConfiguration,
QueueSinkProcessState,
QueueSinkScript,
UpdateQueueSinkOperation,
UpdateQueueSinkOperationResult,
)
from ravendb.documents.smuggler.common import (
DatabaseItemType,
DatabaseRecordItemType,
DatabaseSmugglerExportOptions,
DatabaseSmugglerImportOptions,
DatabaseSmugglerOptions,
ExportCompressionAlgorithm,
)
from ravendb.documents.smuggler.database_smuggler import DatabaseSmuggler
from ravendb.documents.operations.cdc_sink.schema import (
CdcSinkSchemaRequest,
CdcSinkSourceColumn,
CdcSinkSourceForeignKey,
CdcSinkSourceSchema,
CdcSinkSourceTable,
GetCdcSinkSchemaOperation,
)
from ravendb.documents.operations.cdc_sink.testing import (
TestCdcSinkMappingOperation,
TestCdcSinkMappingRequest,
TestCdcSinkMappingResult,
TestCdcSinkOperation,
TestCdcSinkRowResult,
TestCdcSinkRowSelector,
)
from ravendb.documents.smuggler.result import (
Counts,
CountsWithLastEtag,
CountsWithLastEtagAndAttachments,
CountsWithSkippedCountAndLastEtag,
CountsWithSkippedCountAndLastEtagAndAttachments,
DatabaseRecordProgress,
SmugglerProgressBase,
SmugglerResult,
)
from ravendb.documents.smuggler.database_smuggler import SmugglerOperation
from ravendb.exceptions.commercial import LicenseLimitException, LimitType
from ravendb.documents.ai.ai_output_options import AiOutputOptions
from ravendb.documents.operations.cdc_sink import (
AddCdcSinkOperation,
AddCdcSinkOperationResult,
CdcColumnMapping,
CdcColumnType,
CdcSinkConfiguration,
CdcSinkEmbeddedTableConfig,
CdcSinkLinkedTableConfig,
CdcSinkOnDeleteConfig,
CdcSinkPostgresSettings,
CdcSinkProcessState,
CdcSinkRelationType,
CdcSinkTableConfig,
CdcSinkTableLoadState,
CdcSinkTaskState,
UpdateCdcSinkOperation,
UpdateCdcSinkOperationResult,
)
from ravendb.documents.operations.revisions import (
RevisionsCollectionConfiguration,
Expand Down Expand Up @@ -356,6 +435,19 @@
GetCertificatesResponse,
PutClientCertificateOperation,
SecurityClearance,
CertificateUsage,
SsoIdentifier,
SsoProvider,
)
from ravendb.serverwide.operations.connection_strings import (
GetServerWideConnectionStringsOperation,
GetServerWideConnectionStringsResult,
PutServerWideConnectionStringOperation,
PutServerWideConnectionStringResult,
RemoveServerWideConnectionStringOperation,
RemoveServerWideConnectionStringResult,
ServerWideConnectionString,
ServerWideConnectionStringUsage,
)
from ravendb.serverwide.operations.common import (
BuildNumber,
Expand Down Expand Up @@ -689,15 +781,6 @@
# IChangesConnectionState

# todo: Smuggler
# DatabaseItemType
# DatabaseRecordItemType
# DatabaseSmuggler
# DatabaseSmugglerExportOptions
# IDatabaseSmugglerExportOptions
# DatabaseSmugglerImportOptions
# IDatabaseSmugglerImportOptions
# DatabaseSmugglerOptions
# IDatabaseSmugglerOptions

# todo: Certificates
# AddDatabaseNodeOperation
Expand Down
1 change: 1 addition & 0 deletions ravendb/documents/ai/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@
"AiMessagePromptFields",
"AiMessagePromptTypes",
]
from ravendb.documents.ai.ai_output_options import AiOutputOptions
48 changes: 48 additions & 0 deletions ravendb/documents/ai/ai_conversation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from datetime import timedelta

from ravendb.documents.ai.ai_answer import AiAnswer, AiConversationStatus
from ravendb.documents.ai.ai_output_options import AiOutputOptions
from ravendb.documents.ai.content_part import ContentPart, TextPart
from ravendb.documents.operations.ai.agents import (
AiAgentActionRequest,
Expand Down Expand Up @@ -43,13 +44,17 @@ def __init__(
conversation_id: str = None,
change_vector: str = None,
debug: Optional[bool] = None,
cancel_pending_action_tools: bool = False,
):
self._store = store
self._agent_id = agent_id
self._options = options or AiConversationCreationOptions()
self._conversation_id = conversation_id
self._change_vector = change_vector
self._debug = debug
# One-shot: the server drops the tool calls still awaiting a response on the next
# run, and the flag clears itself once that run succeeds.
self._cancel_pending_action_tools = cancel_pending_action_tools

self._prompt_parts: List[ContentPart] = []
self._action_responses: Dict[str, AiAgentActionResponse] = {}
Expand Down Expand Up @@ -145,16 +150,56 @@ def run(self) -> AiAnswer:
if self._handle_server_reply(r):
return r

def run_with_schema(self, output_options: "AiOutputOptions") -> AiAnswer:
"""
Runs one turn with the output format overridden for that turn only, leaving the
agent's own schema in place for later turns. Pass
``AiOutputOptions(no_schema=True)`` to get free-form text back instead of JSON.
"""
if output_options is None:
raise ValueError("output_options cannot be None")

self._dispatched_tool_ids.clear()

while True:
r = self._run_internal(output_options=output_options)
if self._handle_server_reply(r):
return r

def stream(self, stream_property_path: str = None, on_chunk: Optional[Callable[[str], None]] = None) -> AiAnswer:
while True:
r = self._run_internal(stream_property_path=stream_property_path, streamed_chunks_callback=on_chunk)
if self._handle_server_reply(r):
return r

def stream_with_schema(
self,
stream_property_path: str = None,
on_chunk: Optional[Callable[[str], None]] = None,
output_options: "AiOutputOptions" = None,
) -> AiAnswer:
"""
Streams one turn with the output format overridden for that turn only.
``stream_property_path`` is ignored when the options ask for no schema, since
free-form text has no property to stream from.
"""
if output_options is None:
raise ValueError("output_options cannot be None")

while True:
r = self._run_internal(
stream_property_path=stream_property_path,
streamed_chunks_callback=on_chunk,
output_options=output_options,
)
if self._handle_server_reply(r):
return r

def _run_internal(
self,
stream_property_path: Optional[str] = None,
streamed_chunks_callback: Optional[Callable[[str], None]] = None,
output_options: Optional["AiOutputOptions"] = None,
) -> AiAnswer:
from ravendb.documents.operations.ai.agents import RunConversationOperation
import time
Expand Down Expand Up @@ -193,7 +238,9 @@ def _run_internal(
stream_property_path=stream_property_path,
streamed_chunks_callback=streamed_chunks_callback,
attachments_commands=self._attachments_commands,
output_options=output_options,
debug=self._debug,
cancel_pending_action_tools=self._cancel_pending_action_tools,
)

try:
Expand All @@ -203,6 +250,7 @@ def _run_internal(

self._change_vector = result.change_vector
self._conversation_id = result.conversation_id
self._cancel_pending_action_tools = False
self._action_requests = result.action_requests or []

return AiAnswer(
Expand Down
36 changes: 34 additions & 2 deletions ravendb/documents/ai/ai_operations.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Dict, Any, Optional, Type
from typing import TYPE_CHECKING, Dict, Any, Optional, Type, Union

import warnings

Expand All @@ -11,7 +11,9 @@
from ravendb.documents.operations.ai.agents import (
AiAgentConfiguration,
AiAgentConfigurationResult,
AiConversationMessagesResult,
GetAiAgentsResponse,
GetConversationMessagesOptions,
)


Expand Down Expand Up @@ -71,13 +73,33 @@ def get_agents(self, agent_id: str = None) -> GetAiAgentsResponse:
operation = GetAiAgentOperation(agent_id)
return self._store.maintenance.send(operation)

def get_conversation_messages(
self, conversation_id_or_parameters: Union[str, "GetConversationMessagesOptions"]
) -> "AiConversationMessagesResult":
"""
Reads messages from an AI conversation. Returns the most recent messages by default.

Args:
conversation_id_or_parameters: The conversation document ID, or a
GetConversationMessagesOptions for full control over paging
(before/after timestamps), page size, and detail level

Returns:
The conversation's messages, cumulative usage, and paging state
"""
from ravendb.documents.operations.ai.agents import GetConversationMessagesOperation

operation = GetConversationMessagesOperation(conversation_id_or_parameters)
return self._store.maintenance.send(operation)

def conversation(
self,
agent_id: str,
conversation_id: str,
creation_options: "AiConversationCreationOptions" = None,
change_vector: str = None,
debug: Optional[bool] = None,
cancel_pending_action_tools: bool = False,
) -> AiConversation:
"""
Creates a new conversation with the specified AI agent.
Expand All @@ -88,12 +110,22 @@ def conversation(
creation_options: Optional creation options for the conversation
change_vector: Optional change vector for concurrency control
debug: Optional flag enabling server-side conversation debugging
cancel_pending_action_tools: Drop the tool calls the conversation is still waiting on
instead of answering them, on the next run. Cleared once that run succeeds.

Returns:
Conversation operations interface for managing the conversation
"""

return AiConversation(self._store, agent_id, creation_options, conversation_id, change_vector, debug)
return AiConversation(
self._store,
agent_id,
creation_options,
conversation_id,
change_vector,
debug,
cancel_pending_action_tools,
)

def conversation_with_id(self, conversation_id: str, change_vector: str = None) -> AiConversation:
"""
Expand Down
72 changes: 72 additions & 0 deletions ravendb/documents/ai/ai_output_options.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from __future__ import annotations

import json
from typing import Any, Dict, Optional


class AiOutputOptions:
"""
Overrides the output format for a single conversation turn.

The agent still needs a default output schema when it is created; these options take
precedence over it, and only for the turn they are passed to.

Give it exactly one of:

- ``sample_object`` - an object the server turns into a JSON schema at request time
- ``output_schema`` - an explicit JSON schema string
- ``no_schema=True`` - no structured output at all, so the model answers in free text
"""

def __init__(
self,
sample_object: Any = None,
output_schema: str = None,
no_schema: bool = False,
):
if no_schema and (sample_object is not None or output_schema is not None):
raise ValueError(
"no_schema asks the model for free-form text, so it cannot be combined with "
"an output schema or a sample object. Drop one of them."
)

if output_schema is not None and (not output_schema or output_schema.isspace()):
raise ValueError("output_schema cannot be empty or whitespace")

self.sample_object = sample_object
# Takes precedence over sample_object when both are set.
self.output_schema = output_schema
self.no_schema = no_schema

def to_json(self) -> Dict[str, Any]:
json_dict = {}

if self.sample_object is not None:
sample = self.sample_object
if callable(getattr(sample, "to_json", None)):
sample = sample.to_json()
# The server reads this as a JSON string, not as a nested object.
json_dict["SampleObject"] = json.dumps(sample)

if self.output_schema is not None:
json_dict["OutputSchema"] = self.output_schema

if self.no_schema:
json_dict["NoSchema"] = True

return json_dict

@classmethod
def from_json(cls, json_dict: Optional[Dict[str, Any]]) -> Optional[AiOutputOptions]:
if not json_dict:
return None

options = cls.__new__(cls)
sample = json_dict.get("SampleObject")
if sample is not None:
options.sample_object = json.loads(sample) if isinstance(sample, str) else sample
else:
options.sample_object = None
options.output_schema = json_dict.get("OutputSchema")
options.no_schema = json_dict.get("NoSchema", False)
return options
Loading
Loading