Skip to content

Commit afcefa1

Browse files
iamprashantiamprashant
authored andcommitted
feat: added agentkit implimenation
1 parent 99f9666 commit afcefa1

34 files changed

Lines changed: 2089 additions & 314 deletions

rapida/__init__.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,8 @@
162162
CreateBulkPhoneCallResponse,
163163
CreatePhoneCallRequest,
164164
CreatePhoneCallResponse,
165-
AssistantTalkInput,
166-
AssistantTalkOutput,
167-
TalkInput,
168-
TalkOutput,
165+
AssistantTalkRequest,
166+
AssistantTalkResponse,
169167
ConversationAssistantMessage,
170168
ConversationConfiguration,
171169
ConversationDirective,
@@ -174,6 +172,10 @@
174172
ConversationToolCall,
175173
ConversationToolResult
176174
)
175+
from rapida.clients.protos.agentkit_pb2 import (
176+
TalkInput,
177+
TalkOutput,
178+
)
177179
from rapida.clients.protos.assistant_analysis_pb2 import (
178180
AssistantAnalysis,
179181
CreateAssistantAnalysisRequest,
@@ -377,12 +379,14 @@
377379
)
378380

379381
from rapida.clients.protos.talk_api_pb2_grpc import (
380-
TalkServiceServicer,
382+
TalkServiceServicer,
383+
)
384+
from rapida.clients.protos.agentkit_pb2_grpc import (
381385
AgentKitStub,
382386
AgentKit,
383387
AgentKitServicer,
384388
add_AgentKitServicer_to_server,
385-
)
389+
)
386390

387391
# Agent Kit classes
388392
from rapida.agentkit import (
@@ -703,8 +707,8 @@
703707
"SSLConfig",
704708
"AuthConfig",
705709
"AuthorizationInterceptor",
706-
"AssistantTalkInput",
707-
"AssistantTalkOutput",
710+
"AssistantTalkRequest",
711+
"AssistantTalkResponse",
708712
"TalkInput",
709713
"TalkOutput",
710714
"ConversationAssistantMessage",

rapida/agentkit/__init__.py

Lines changed: 90 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,31 @@
3333
- Tool execution
3434
- Conversation logic
3535
36+
Flow (mirrors the WebTalk/WebRTC flow):
37+
1. Rapida sends ConversationInitialization — always the first message.
38+
Acknowledge it with initialization_response().
39+
2. Rapida may send ConversationConfiguration to change stream mode.
40+
Acknowledge it with configuration_response().
41+
3. Rapida sends ConversationUserMessage for each user turn.
42+
Reply with assistant_response() chunks.
43+
3644
Usage:
3745
from rapida import AgentKitServer, AgentKitAgent
38-
46+
3947
class MyAgent(AgentKitAgent):
4048
def Talk(self, request_iterator, context):
4149
for request in request_iterator:
42-
if request.HasField("configuration"):
50+
if request.HasField("initialization"):
51+
# Always first — acknowledge and set up your session
52+
yield self.initialization_response(request.initialization)
53+
elif request.HasField("configuration"):
4354
yield self.configuration_response(request.configuration)
4455
elif request.HasField("message"):
4556
msg = request.message
4657
# Your LLM logic here
4758
yield self.assistant_response(msg.id, "Hello!", completed=False)
4859
yield self.assistant_response(msg.id, "Hello!", completed=True)
49-
60+
5061
server = AgentKitServer(
5162
agent=MyAgent(),
5263
port=50051,
@@ -67,20 +78,21 @@ def Talk(self, request_iterator, context):
6778
from grpc import ServerInterceptor
6879

6980
from rapida.clients.protos.talk_api_pb2 import (
81+
ConversationInitialization,
7082
ConversationConfiguration,
7183
ConversationAssistantMessage,
7284
ConversationDirective,
7385
ConversationToolCall,
7486
ConversationToolResult,
7587
)
76-
from rapida.clients.protos.talk_api_pb2 import (
88+
from rapida.clients.protos.agentkit_pb2 import (
7789
TalkInput,
7890
TalkOutput,
7991
)
8092
from rapida.clients.protos.common_pb2 import (
8193
Error,
8294
)
83-
from rapida.clients.protos.talk_api_pb2_grpc import (
95+
from rapida.clients.protos.agentkit_pb2_grpc import (
8496
AgentKitServicer,
8597
add_AgentKitServicer_to_server,
8698
)
@@ -184,11 +196,20 @@ class AgentKitAgent(AgentKitServicer):
184196
185197
Subclass this and implement Talk() with your LLM logic.
186198
199+
The message flow mirrors WebTalk/WebRTC:
200+
1. ConversationInitialization — always the first message. Acknowledge it.
201+
2. ConversationConfiguration — optional mode change. Acknowledge it.
202+
3. ConversationUserMessage — user turns. Reply with assistant_response().
203+
187204
Example:
188205
class MyAgent(AgentKitAgent):
189206
def Talk(self, request_iterator, context):
190207
for request in request_iterator:
191-
if request.HasField("configuration"):
208+
if request.HasField("initialization"):
209+
# Always first — set up your session here
210+
conv_id = self.get_conversation_id(request)
211+
yield self.initialization_response(request.initialization)
212+
elif request.HasField("configuration"):
192213
yield self.configuration_response(request.configuration)
193214
elif request.HasField("message"):
194215
msg = request.message
@@ -218,19 +239,41 @@ def response(
218239
"""
219240
return TalkOutput(code=code, success=success, **kwargs)
220241

242+
def initialization_response(
243+
self, initialization: ConversationInitialization
244+
) -> TalkOutput:
245+
"""
246+
Acknowledge a ConversationInitialization from Rapida.
247+
248+
This should always be the first response yielded in Talk().
249+
Rapida always sends initialization as the first message on the stream,
250+
mirroring the WebTalk/WebRTC flow.
251+
252+
Args:
253+
initialization: The ConversationInitialization received from Rapida
254+
255+
Returns:
256+
TalkOutput acknowledging the initialization
257+
"""
258+
return self.response(initialization=initialization)
259+
221260
def configuration_response(
222-
self, configuration: ConversationConfiguration
261+
self, configuration: ConversationConfiguration = None
223262
) -> TalkOutput:
224263
"""
225264
Acknowledge a configuration request from Rapida.
226265
266+
Note: TalkOutput has no configuration field in its data oneof, so this
267+
sends a plain code-200 acknowledgement with no data payload.
268+
Configuration changes do not carry a data ack in the AgentKit protocol.
269+
227270
Args:
228-
configuration: The configuration received from the request
271+
configuration: Unused; kept for API compatibility.
229272
230273
Returns:
231-
TalkOutput acknowledging the configuration
274+
TalkOutput with code=200 and no data payload
232275
"""
233-
return self.response(configuration=configuration)
276+
return self.response()
234277

235278
def assistant_response(
236279
self, msg_id: str, content: str, completed: bool = False
@@ -419,6 +462,38 @@ def get_message_id(self, request: TalkInput) -> Optional[str]:
419462
return request.message.id
420463
return None
421464

465+
def get_conversation_id(self, request: TalkInput) -> Optional[int]:
466+
"""
467+
Extract the conversation ID from an initialization request.
468+
469+
Args:
470+
request: The incoming initialization request from Rapida
471+
472+
Returns:
473+
Conversation ID, or None if not an initialization request
474+
"""
475+
if request.HasField("initialization"):
476+
return request.initialization.assistantConversationId
477+
return None
478+
479+
def get_assistant_id(self, request: TalkInput) -> Optional[int]:
480+
"""
481+
Extract the assistant ID from an initialization request.
482+
483+
Args:
484+
request: The incoming initialization request from Rapida
485+
486+
Returns:
487+
Assistant ID, or None if not an initialization request or assistant is unset
488+
"""
489+
if request.HasField("initialization") and request.initialization.HasField("assistant"):
490+
return request.initialization.assistant.assistantId
491+
return None
492+
493+
def is_initialization_request(self, request: TalkInput) -> bool:
494+
"""Check if request is the initial ConversationInitialization message."""
495+
return request.HasField("initialization")
496+
422497
def is_configuration_request(self, request: TalkInput) -> bool:
423498
"""Check if request is a configuration request."""
424499
return request.HasField("configuration")
@@ -464,14 +539,17 @@ class AgentKitServer:
464539
class MyAgent(AgentKitAgent):
465540
def Talk(self, request_iterator, context):
466541
for request in request_iterator:
467-
if self.is_configuration_request(request):
542+
if self.is_initialization_request(request):
543+
# Always first — set up your session here
544+
yield self.initialization_response(request.initialization)
545+
elif self.is_configuration_request(request):
468546
yield self.configuration_response(request.configuration)
469547
elif self.is_text_message(request):
470548
msg_id = self.get_message_id(request)
471549
text = self.get_user_text(request)
472550
# Your LLM logic here
473551
yield self.assistant_response(msg_id, "Hello!", completed=True)
474-
552+
475553
server = AgentKitServer(agent=MyAgent(), port=50051)
476554
server.start()
477555
server.wait_for_termination()

rapida/clients/protos/agentkit_pb2.py

Lines changed: 43 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import rapida.clients.protos.common_pb2 as _common_pb2
2+
import rapida.clients.protos.talk_api_pb2 as _talk_api_pb2
3+
from google.protobuf import descriptor as _descriptor
4+
from google.protobuf import message as _message
5+
from collections.abc import Mapping as _Mapping
6+
from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union
7+
8+
DESCRIPTOR: _descriptor.FileDescriptor
9+
10+
class TalkInput(_message.Message):
11+
__slots__ = ("initialization", "configuration", "message", "interruption", "metadata", "metric")
12+
INITIALIZATION_FIELD_NUMBER: _ClassVar[int]
13+
CONFIGURATION_FIELD_NUMBER: _ClassVar[int]
14+
MESSAGE_FIELD_NUMBER: _ClassVar[int]
15+
INTERRUPTION_FIELD_NUMBER: _ClassVar[int]
16+
METADATA_FIELD_NUMBER: _ClassVar[int]
17+
METRIC_FIELD_NUMBER: _ClassVar[int]
18+
initialization: _talk_api_pb2.ConversationInitialization
19+
configuration: _talk_api_pb2.ConversationConfiguration
20+
message: _talk_api_pb2.ConversationUserMessage
21+
interruption: _talk_api_pb2.ConversationInterruption
22+
metadata: _talk_api_pb2.ConversationMetadata
23+
metric: _talk_api_pb2.ConversationMetric
24+
def __init__(self, initialization: _Optional[_Union[_talk_api_pb2.ConversationInitialization, _Mapping]] = ..., configuration: _Optional[_Union[_talk_api_pb2.ConversationConfiguration, _Mapping]] = ..., message: _Optional[_Union[_talk_api_pb2.ConversationUserMessage, _Mapping]] = ..., interruption: _Optional[_Union[_talk_api_pb2.ConversationInterruption, _Mapping]] = ..., metadata: _Optional[_Union[_talk_api_pb2.ConversationMetadata, _Mapping]] = ..., metric: _Optional[_Union[_talk_api_pb2.ConversationMetric, _Mapping]] = ...) -> None: ...
25+
26+
class TalkOutput(_message.Message):
27+
__slots__ = ("code", "success", "initialization", "interruption", "assistant", "tool", "toolResult", "directive", "error")
28+
CODE_FIELD_NUMBER: _ClassVar[int]
29+
SUCCESS_FIELD_NUMBER: _ClassVar[int]
30+
INITIALIZATION_FIELD_NUMBER: _ClassVar[int]
31+
INTERRUPTION_FIELD_NUMBER: _ClassVar[int]
32+
ASSISTANT_FIELD_NUMBER: _ClassVar[int]
33+
TOOL_FIELD_NUMBER: _ClassVar[int]
34+
TOOLRESULT_FIELD_NUMBER: _ClassVar[int]
35+
DIRECTIVE_FIELD_NUMBER: _ClassVar[int]
36+
ERROR_FIELD_NUMBER: _ClassVar[int]
37+
code: int
38+
success: bool
39+
initialization: _talk_api_pb2.ConversationInitialization
40+
interruption: _talk_api_pb2.ConversationInterruption
41+
assistant: _talk_api_pb2.ConversationAssistantMessage
42+
tool: _talk_api_pb2.ConversationToolCall
43+
toolResult: _talk_api_pb2.ConversationToolResult
44+
directive: _talk_api_pb2.ConversationDirective
45+
error: _common_pb2.Error
46+
def __init__(self, code: _Optional[int] = ..., success: bool = ..., initialization: _Optional[_Union[_talk_api_pb2.ConversationInitialization, _Mapping]] = ..., interruption: _Optional[_Union[_talk_api_pb2.ConversationInterruption, _Mapping]] = ..., assistant: _Optional[_Union[_talk_api_pb2.ConversationAssistantMessage, _Mapping]] = ..., tool: _Optional[_Union[_talk_api_pb2.ConversationToolCall, _Mapping]] = ..., toolResult: _Optional[_Union[_talk_api_pb2.ConversationToolResult, _Mapping]] = ..., directive: _Optional[_Union[_talk_api_pb2.ConversationDirective, _Mapping]] = ..., error: _Optional[_Union[_common_pb2.Error, _Mapping]] = ...) -> None: ...

0 commit comments

Comments
 (0)