interloper-api 0.103.0__tar.gz → 0.105.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {interloper_api-0.103.0 → interloper_api-0.105.0}/PKG-INFO +1 -1
- {interloper_api-0.103.0 → interloper_api-0.105.0}/pyproject.toml +1 -1
- {interloper_api-0.103.0 → interloper_api-0.105.0}/pyproject.toml.orig +1 -1
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/routes/agent.py +86 -9
- {interloper_api-0.103.0 → interloper_api-0.105.0}/README.md +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/__init__.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/app.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/dependencies/__init__.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/dependencies/auth.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/dependencies/rbac.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/dependencies/state.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/notifications/__init__.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/notifications/base.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/notifications/invitations.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/notifications/super_admins.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/routes/__init__.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/routes/admin.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/routes/auth.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/routes/backfills.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/routes/catalog.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/routes/components.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/routes/executions.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/routes/health.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/routes/oauth.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/routes/organisations.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/routes/overview.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/routes/relations.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/routes/runs.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/routes/tokens.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/routes/websocket.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/utils/__init__.py +0 -0
- {interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/utils/time.py +0 -0
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
A conversation is a member's own: its history lives in the store and the
|
|
4
4
|
server, not the client, is its source of truth. Each turn is one
|
|
5
5
|
:meth:`~pydantic_ai.ui.vercel_ai.VercelAIAdapter.dispatch_request`: the
|
|
6
|
-
|
|
7
|
-
the
|
|
8
|
-
|
|
6
|
+
stored history is passed as ``message_history``, the client's request adds
|
|
7
|
+
the new prompt (or the tool approvals and answers the app collected), and the
|
|
8
|
+
whole history is saved back when the run completes.
|
|
9
9
|
|
|
10
10
|
Available when ``interloper-agent`` is installed.
|
|
11
11
|
"""
|
|
@@ -13,7 +13,8 @@ Available when ``interloper-agent`` is installed.
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
15
|
import logging
|
|
16
|
-
from collections.abc import AsyncIterator
|
|
16
|
+
from collections.abc import AsyncIterator, Sequence
|
|
17
|
+
from dataclasses import dataclass
|
|
17
18
|
from datetime import datetime
|
|
18
19
|
from typing import Annotated, Any
|
|
19
20
|
from uuid import UUID
|
|
@@ -24,8 +25,17 @@ from interloper_db import Page, PageQuery
|
|
|
24
25
|
from interloper_db.models import Conversation
|
|
25
26
|
from interloper_toolkit import ToolkitContext
|
|
26
27
|
from pydantic import BaseModel
|
|
28
|
+
from pydantic_ai import DeferredToolResults
|
|
27
29
|
from pydantic_ai.agent import AgentRunResult
|
|
28
|
-
from pydantic_ai.messages import
|
|
30
|
+
from pydantic_ai.messages import (
|
|
31
|
+
ModelMessage,
|
|
32
|
+
ModelMessagesTypeAdapter,
|
|
33
|
+
ModelRequest,
|
|
34
|
+
RetryPromptPart,
|
|
35
|
+
ToolCallPart,
|
|
36
|
+
ToolReturnPart,
|
|
37
|
+
UserPromptPart,
|
|
38
|
+
)
|
|
29
39
|
from pydantic_ai.ui import UIEventStream
|
|
30
40
|
from pydantic_ai.ui.vercel_ai import VercelAIAdapter, VercelAIEventStream
|
|
31
41
|
from pydantic_ai.ui.vercel_ai.request_types import RequestData, UIMessage
|
|
@@ -62,8 +72,73 @@ class LoggedEventStream(VercelAIEventStream[ToolkitContext, Any]):
|
|
|
62
72
|
yield chunk
|
|
63
73
|
|
|
64
74
|
|
|
65
|
-
|
|
66
|
-
|
|
75
|
+
@dataclass
|
|
76
|
+
class ConversationAdapter(VercelAIAdapter[ToolkitContext, Any]):
|
|
77
|
+
"""The Vercel adapter for a stored conversation.
|
|
78
|
+
|
|
79
|
+
The app sends its whole message list on every turn, while the stored
|
|
80
|
+
``history`` already holds all of it but what the turn adds. Taking the
|
|
81
|
+
client's copies too would store every earlier message again, so only the
|
|
82
|
+
additions are kept: the final message's prompt, and the answers to calls
|
|
83
|
+
the history leaves pending. Approvals reach the run as deferred results.
|
|
84
|
+
It also builds :class:`LoggedEventStream`.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
history: Sequence[ModelMessage] = ()
|
|
88
|
+
|
|
89
|
+
def sanitize_messages(
|
|
90
|
+
self, messages: Sequence[ModelMessage], *, deferred_tool_results: DeferredToolResults | None = None
|
|
91
|
+
) -> list[ModelMessage]:
|
|
92
|
+
"""Keep what the client's messages add to the stored history, sanitized.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
messages: The client's messages, loaded from the request.
|
|
96
|
+
deferred_tool_results: The approvals the request carries, if any.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
One request holding the new answers and prompt, or nothing when the
|
|
100
|
+
client added neither.
|
|
101
|
+
"""
|
|
102
|
+
additions = self._additions(messages)
|
|
103
|
+
return super().sanitize_messages(additions, deferred_tool_results=deferred_tool_results)
|
|
104
|
+
|
|
105
|
+
def _additions(self, messages: Sequence[ModelMessage]) -> list[ModelMessage]:
|
|
106
|
+
"""The client's answers to pending calls, then its new prompt.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
messages: The client's messages, loaded from the request.
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
One request holding the additions, or an empty list.
|
|
113
|
+
"""
|
|
114
|
+
called = {part.tool_call_id for part in self._parts(self.history, ToolCallPart)}
|
|
115
|
+
answered = {part.tool_call_id for part in self._parts(self.history, (ToolReturnPart, RetryPromptPart))}
|
|
116
|
+
answers = [
|
|
117
|
+
part
|
|
118
|
+
for part in self._parts(messages, (ToolReturnPart, RetryPromptPart))
|
|
119
|
+
if part.tool_call_id in called - answered
|
|
120
|
+
]
|
|
121
|
+
final = messages[-1] if messages else None
|
|
122
|
+
prompts = (
|
|
123
|
+
[part for part in final.parts if isinstance(part, UserPromptPart)]
|
|
124
|
+
if isinstance(final, ModelRequest)
|
|
125
|
+
else []
|
|
126
|
+
)
|
|
127
|
+
parts = [*answers, *prompts]
|
|
128
|
+
return [ModelRequest(parts=parts)] if parts else []
|
|
129
|
+
|
|
130
|
+
@staticmethod
|
|
131
|
+
def _parts(messages: Sequence[ModelMessage], part_type: type | tuple[type, ...]) -> list[Any]:
|
|
132
|
+
"""The parts of a type, across the messages.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
messages: The messages to read.
|
|
136
|
+
part_type: The part class, or classes, to collect.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
The matching parts, in order.
|
|
140
|
+
"""
|
|
141
|
+
return [part for message in messages for part in message.parts if isinstance(part, part_type)]
|
|
67
142
|
|
|
68
143
|
def build_event_stream(self) -> UIEventStream[RequestData, BaseChunk, ToolkitContext, Any]:
|
|
69
144
|
"""Build the event stream that also logs failures.
|
|
@@ -253,12 +328,14 @@ async def chat(
|
|
|
253
328
|
len(messages),
|
|
254
329
|
)
|
|
255
330
|
|
|
256
|
-
|
|
331
|
+
history = ModelMessagesTypeAdapter.validate_python(conversation.messages)
|
|
332
|
+
return await ConversationAdapter.dispatch_request(
|
|
257
333
|
request,
|
|
258
334
|
agent=agent,
|
|
259
335
|
sdk_version=SDK_VERSION,
|
|
260
336
|
deps=context,
|
|
261
|
-
|
|
337
|
+
history=history,
|
|
338
|
+
message_history=history,
|
|
262
339
|
conversation_id=str(conversation.id),
|
|
263
340
|
usage_limits=TURN_LIMITS,
|
|
264
341
|
on_complete=save,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/dependencies/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/notifications/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/notifications/invitations.py
RENAMED
|
File without changes
|
{interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/notifications/super_admins.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{interloper_api-0.103.0 → interloper_api-0.105.0}/src/interloper_api/routes/organisations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|