chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
__pycache__/
|
||||
venv/
|
||||
.venv/
|
||||
dist/
|
||||
@@ -0,0 +1,76 @@
|
||||
# Rowboat Python SDK
|
||||
|
||||
A Python SDK for interacting with the Rowboat API.
|
||||
|
||||
## Installation
|
||||
|
||||
You can install the package using pip:
|
||||
|
||||
```bash
|
||||
pip install rowboat
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Usage
|
||||
|
||||
The main way to interact with Rowboat is using the `Client` class, which provides a stateless chat API. You can manage conversation state using the `conversationId` returned in each response.
|
||||
|
||||
```python
|
||||
from rowboat.client import Client
|
||||
from rowboat.schema import UserMessage
|
||||
|
||||
# Initialize the client
|
||||
client = Client(
|
||||
host="<HOST>",
|
||||
projectId="<PROJECT_ID>",
|
||||
apiKey="<API_KEY>"
|
||||
)
|
||||
|
||||
# Start a new conversation
|
||||
result = client.run_turn(
|
||||
messages=[
|
||||
UserMessage(role='user', content="list my github repos")
|
||||
]
|
||||
)
|
||||
print(result.turn.output[-1].content)
|
||||
print("Conversation ID:", result.conversationId)
|
||||
|
||||
# Continue the conversation by passing the conversationId
|
||||
result = client.run_turn(
|
||||
messages=[
|
||||
UserMessage(role='user', content="how many did you find?")
|
||||
],
|
||||
conversationId=result.conversationId
|
||||
)
|
||||
print(result.turn.output[-1].content)
|
||||
```
|
||||
|
||||
### Using Tool Overrides (Mock Tools)
|
||||
|
||||
You can provide tool override instructions to test a specific configuration using the `mockTools` argument:
|
||||
|
||||
```python
|
||||
result = client.run_turn(
|
||||
messages=[
|
||||
UserMessage(role='user', content="What's the weather?")
|
||||
],
|
||||
mockTools={
|
||||
"weather_lookup": "The weather in any city is sunny and 25°C.",
|
||||
"calculator": "The result of any calculation is 42."
|
||||
}
|
||||
)
|
||||
print(result.turn.output[-1].content)
|
||||
```
|
||||
|
||||
### Message Types
|
||||
|
||||
You can use different message types as defined in `rowboat.schema`, such as `UserMessage`, `SystemMessage`, etc. See `schema.py` for all available message types.
|
||||
|
||||
### Error Handling
|
||||
|
||||
If the API returns a non-200 status code, a `ValueError` will be raised with the error details.
|
||||
|
||||
---
|
||||
|
||||
For more advanced usage, see the docstrings in `client.py` and the message schemas in `schema.py`.
|
||||
@@ -0,0 +1,26 @@
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "rowboat"
|
||||
version = "5.0.1"
|
||||
authors = [
|
||||
{ name = "Ramnique Singh", email = "ramnique@rowboatlabs.com" },
|
||||
]
|
||||
description = "Python sdk for the Rowboat API"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.7"
|
||||
classifiers = [
|
||||
"Programming Language :: Python :: 3",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
"Operating System :: OS Independent",
|
||||
]
|
||||
dependencies = [
|
||||
"requests>=2.25.0",
|
||||
"pydantic>=2.0.0",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
"Homepage" = "https://github.com/rowboatlabs/rowboat/tree/main/apps/python-sdk"
|
||||
"Bug Tracker" = "https://github.com/rowboatlabs/rowboat/issues"
|
||||
@@ -0,0 +1,9 @@
|
||||
annotated-types==0.7.0
|
||||
certifi==2024.12.14
|
||||
charset-normalizer==3.4.1
|
||||
idna==3.10
|
||||
pydantic==2.10.5
|
||||
pydantic_core==2.27.2
|
||||
requests==2.32.3
|
||||
typing_extensions==4.12.2
|
||||
urllib3==2.3.0
|
||||
@@ -0,0 +1,11 @@
|
||||
from .client import Client
|
||||
from .schema import (
|
||||
ApiMessage,
|
||||
UserMessage,
|
||||
SystemMessage,
|
||||
AssistantMessage,
|
||||
AssistantMessageWithToolCalls,
|
||||
ToolMessage,
|
||||
ApiRequest,
|
||||
ApiResponse
|
||||
)
|
||||
@@ -0,0 +1,73 @@
|
||||
from typing import Dict, List, Optional
|
||||
import requests
|
||||
from .schema import (
|
||||
ApiRequest,
|
||||
ApiResponse,
|
||||
ApiMessage,
|
||||
UserMessage,
|
||||
)
|
||||
|
||||
class Client:
|
||||
def __init__(self, host: str, projectId: str, apiKey: str) -> None:
|
||||
self.base_url: str = f'{host}/api/v1/{projectId}/chat'
|
||||
self.headers: Dict[str, str] = {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': f'Bearer {apiKey}'
|
||||
}
|
||||
|
||||
def _call_api(
|
||||
self,
|
||||
messages: List[ApiMessage],
|
||||
conversationId: Optional[str] = None,
|
||||
mockTools: Optional[Dict[str, str]] = None
|
||||
) -> ApiResponse:
|
||||
request = ApiRequest(
|
||||
messages=messages,
|
||||
conversationId=conversationId,
|
||||
mockTools=mockTools
|
||||
)
|
||||
json_data = request.model_dump()
|
||||
response = requests.post(self.base_url, headers=self.headers, json=json_data)
|
||||
|
||||
if not response.status_code == 200:
|
||||
raise ValueError(f"Error: {response.status_code} - {response.text}")
|
||||
|
||||
return ApiResponse.model_validate(response.json())
|
||||
|
||||
def run_turn(
|
||||
self,
|
||||
messages: List[ApiMessage],
|
||||
conversationId: Optional[str] = None,
|
||||
mockTools: Optional[Dict[str, str]] = None,
|
||||
) -> ApiResponse:
|
||||
"""Stateless chat method that handles a single conversation turn"""
|
||||
|
||||
# call api
|
||||
return self._call_api(
|
||||
messages=messages,
|
||||
conversationId=conversationId,
|
||||
mockTools=mockTools,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
host: str = "<HOST>"
|
||||
project_id: str = "<PROJECT_ID>"
|
||||
api_key: str = "<API_KEY>"
|
||||
client = Client(host, project_id, api_key)
|
||||
|
||||
result = client.run_turn(
|
||||
messages=[
|
||||
UserMessage(role='user', content="list my github repos")
|
||||
]
|
||||
)
|
||||
print(result.turn.output[-1].content)
|
||||
print(result.conversationId)
|
||||
|
||||
result = client.run_turn(
|
||||
messages=[
|
||||
UserMessage(role='user', content="how many did you find?")
|
||||
],
|
||||
conversationId=result.conversationId
|
||||
)
|
||||
print(result.turn.output[-1].content)
|
||||
@@ -0,0 +1,58 @@
|
||||
from typing import List, Optional, Union, Literal, Dict
|
||||
from pydantic import BaseModel
|
||||
|
||||
class SystemMessage(BaseModel):
|
||||
role: Literal['system']
|
||||
content: str
|
||||
|
||||
class UserMessage(BaseModel):
|
||||
role: Literal['user']
|
||||
content: str
|
||||
|
||||
class AssistantMessage(BaseModel):
|
||||
role: Literal['assistant']
|
||||
content: str
|
||||
agenticName: Optional[str] = None
|
||||
responseType: Literal['internal', 'external']
|
||||
|
||||
class FunctionCall(BaseModel):
|
||||
name: str
|
||||
arguments: str
|
||||
|
||||
class ToolCall(BaseModel):
|
||||
id: str
|
||||
type: Literal['function']
|
||||
function: FunctionCall
|
||||
|
||||
class AssistantMessageWithToolCalls(BaseModel):
|
||||
role: Literal['assistant']
|
||||
content: Optional[str] = None
|
||||
toolCalls: List[ToolCall]
|
||||
agenticName: Optional[str] = None
|
||||
|
||||
class ToolMessage(BaseModel):
|
||||
role: Literal['tool']
|
||||
content: str
|
||||
toolCallId: str
|
||||
toolName: str
|
||||
|
||||
ApiMessage = Union[
|
||||
SystemMessage,
|
||||
UserMessage,
|
||||
AssistantMessage,
|
||||
AssistantMessageWithToolCalls,
|
||||
ToolMessage
|
||||
]
|
||||
|
||||
class Turn(BaseModel):
|
||||
id: str
|
||||
output: List[ApiMessage]
|
||||
|
||||
class ApiRequest(BaseModel):
|
||||
conversationId: Optional[str] = None
|
||||
messages: List[ApiMessage]
|
||||
mockTools: Optional[Dict[str, str]] = None
|
||||
|
||||
class ApiResponse(BaseModel):
|
||||
conversationId: str
|
||||
turn: Turn
|
||||
Reference in New Issue
Block a user