Skip to content

Commit 0ee7278

Browse files
authored
Merge pull request #32 from DIMO-Network/DVR-825
DVR-825 → Prep updates for Conversations API
2 parents 02809f8 + ec14ef1 commit 0ee7278

4 files changed

Lines changed: 170 additions & 57 deletions

File tree

README.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,29 @@ pip install dimo-python-sdk
1010

1111
## Unit Testing
1212

13-
Coming Soon
13+
The SDK includes comprehensive unit tests to ensure reliability and correctness. To run the tests:
14+
15+
1. **Install dependencies:**
16+
```bash
17+
pip install -r requirements.txt
18+
```
19+
20+
2. **Run all tests:**
21+
```bash
22+
pytest
23+
```
24+
25+
3. **Run tests with verbose output:**
26+
```bash
27+
pytest -v
28+
```
29+
30+
4. **Run specific test files:**
31+
```bash
32+
pytest tests/test_conversations.py -v
33+
```
34+
35+
The test suite uses `pytest` and includes tests for all major SDK functionality including authentication, API endpoints, GraphQL queries, and error handling
1436

1537
## API Documentation
1638

dimo/api/conversations.py

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,25 +44,29 @@ def health_check(self) -> Dict:
4444
def create_agent(
4545
self,
4646
developer_jwt: str,
47-
user: str,
48-
vehicle_ids: Optional[List[int]] = None,
47+
api_key: str,
48+
user_wallet: str,
49+
agent_type: str,
50+
vehicle_ids: Optional[str] = None,
51+
personality: str = "uncle_mechanic",
4952
) -> Dict:
5053
"""
51-
Create a new conversational agent for a user with optional vehicle access.
54+
Create a new conversational agent with the specified configuration.
5255
5356
Args:
5457
developer_jwt (str): Developer JWT token for authentication
55-
user (str): Wallet address (0x...) or email identifying the user
56-
vehicle_ids (list[int], optional): List of vehicle token IDs this agent can access.
57-
- None (default): Unrestricted access, ownership validated at runtime
58-
- []: Empty list means no vehicle access (identity queries only)
59-
- [872, 1234]: Explicit list of allowed vehicles
58+
api_key (str): DIMO API key for the agent to access vehicle data
59+
user_wallet (str): User's wallet address (e.g., "0x2345...")
60+
agent_type (str): The type of agent to create (e.g., "driver_agent_v1")
61+
vehicle_ids (str, optional): JSON array string of vehicle token IDs (e.g., "[1, 2, 3]").
62+
If not provided, agent will have access to all vehicles owned by the user.
63+
personality (str, optional): Personality preset for the agent. Defaults to "uncle_mechanic"
6064
6165
Returns:
62-
dict: Agent information including agentId, mode, user, vehicleIds, and createdAt
66+
dict: Agent information including agentId and configuration details
6367
6468
Behavior:
65-
- One agent per user (idempotent creation)
69+
- Creates a new agent with the specified type and configuration
6670
- Validates configuration and mode detection
6771
- Creates/reuses shared identity subagent
6872
- Creates per-vehicle telemetry subagents with token exchange
@@ -73,20 +77,31 @@ def create_agent(
7377
>>> dev_jwt = "your_developer_jwt"
7478
>>> agent = dimo.conversations.create_agent(
7579
... developer_jwt=dev_jwt,
76-
... user="0x1234567890abcdef1234567890abcdef12345678",
77-
... vehicle_ids=[872, 1234],
80+
... api_key="0x1234567890abcdef...",
81+
... user_wallet="0x86b04f6d1D9E79aD7eB31cDEAF37442B00d64605",
82+
... agent_type="driver_agent_v1",
83+
... vehicle_ids="[1, 2, 3]",
7884
... )
7985
>>> print(agent['agentId'])
8086
"""
8187
check_type("developer_jwt", developer_jwt, str)
82-
check_type("user", user, str)
83-
check_optional_type("vehicle_ids", vehicle_ids, list)
84-
# check_type("enable_websearch", enable_websearch, bool)
88+
check_type("api_key", api_key, str)
89+
check_type("user_wallet", user_wallet, str)
90+
check_optional_type("vehicle_ids", vehicle_ids, str)
91+
check_type("agent_type", agent_type, str)
92+
check_type("personality", personality, str)
93+
94+
# Build variables dict
95+
variables = {"USER_WALLET": user_wallet}
96+
if vehicle_ids is not None:
97+
variables["VEHICLE_IDS"] = vehicle_ids
8598

99+
# Build request body
86100
body = {
87-
"user": user,
88-
"vehicleIds": vehicle_ids,
89-
# "enableWebsearch": enable_websearch,
101+
"personality": personality,
102+
"secrets": {"DIMO_API_KEY": api_key},
103+
"type": agent_type,
104+
"variables": variables,
90105
}
91106

92107
response = self._request(

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "dimo-python-sdk"
7-
version = "1.7.0"
7+
version = "1.7.1"
88
authors = [
99
{ name="Barrett Kowalsky", email="barrettkowalsky@gmail.com" },
1010
]

tests/test_conversations.py

Lines changed: 113 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -47,25 +47,27 @@ class TestConversationsCreateAgent:
4747
"""Test the create_agent endpoint."""
4848

4949
def test_create_agent_minimal(self, monkeypatch):
50-
"""Test creating an agent with minimal parameters."""
50+
"""Test creating an agent with minimal required parameters (no vehicle_ids)."""
5151
client = DIMO(env="Dev")
5252

5353
# Mock the request method
5454
fake_request = MagicMock(return_value={
5555
"agentId": "agent-abc123",
56-
"user": "0x1234567890abcdef1234567890abcdef12345678",
57-
"vehicleIds": None,
58-
"mode": "unrestricted",
56+
"type": "driver_agent_v1",
57+
"personality": "uncle_mechanic",
5958
"createdAt": "2024-01-01T00:00:00Z"
6059
})
6160
monkeypatch.setattr(client, "request", fake_request)
6261

6362
dev_jwt = "test_developer_jwt"
64-
user = "0x1234567890abcdef1234567890abcdef12345678"
63+
api_key = "0x1234567890abcdef"
64+
user_wallet = "0x86b04f6d1D9E79aD7eB31cDEAF37442B00d64605"
6565

6666
result = client.conversations.create_agent(
6767
developer_jwt=dev_jwt,
68-
user=user
68+
api_key=api_key,
69+
user_wallet=user_wallet,
70+
agent_type="driver_agent_v1"
6971
)
7072

7173
# Verify the request was called correctly
@@ -75,66 +77,104 @@ def test_create_agent_minimal(self, monkeypatch):
7577
assert args[0] == "POST"
7678
assert args[1] == "Conversations"
7779
assert args[2] == "/agents"
78-
assert kwargs["data"]["user"] == user
79-
assert kwargs["data"]["vehicleIds"] is None
80+
assert kwargs["data"]["type"] == "driver_agent_v1"
81+
assert kwargs["data"]["personality"] == "uncle_mechanic"
82+
assert kwargs["data"]["secrets"]["DIMO_API_KEY"] == api_key
83+
assert kwargs["data"]["variables"]["USER_WALLET"] == user_wallet
84+
assert "VEHICLE_IDS" not in kwargs["data"]["variables"]
8085

8186
# Verify the response
8287
assert result["agentId"] == "agent-abc123"
83-
assert result["user"] == user
84-
assert result["mode"] == "unrestricted"
88+
assert result["type"] == "driver_agent_v1"
8589

8690
def test_create_agent_with_vehicle_ids(self, monkeypatch):
8791
"""Test creating an agent with specific vehicle IDs."""
8892
client = DIMO(env="Dev")
8993

9094
fake_request = MagicMock(return_value={
9195
"agentId": "agent-def456",
92-
"user": "user@example.com",
93-
"vehicleIds": [872, 1234],
94-
"mode": "restricted",
96+
"type": "driver_agent_v1",
9597
"createdAt": "2024-01-01T00:00:00Z"
9698
})
9799
monkeypatch.setattr(client, "request", fake_request)
98100

99101
dev_jwt = "test_developer_jwt"
100-
user = "user@example.com"
101-
vehicle_ids = [872, 1234]
102+
api_key = "0xabcdef123456"
103+
user_wallet = "0x86b04f6d1D9E79aD7eB31cDEAF37442B00d64605"
104+
vehicle_ids = "[872, 1234]"
102105

103106
result = client.conversations.create_agent(
104107
developer_jwt=dev_jwt,
105-
user=user,
108+
api_key=api_key,
109+
user_wallet=user_wallet,
110+
agent_type="driver_agent_v1",
106111
vehicle_ids=vehicle_ids
107112
)
108113

109114
# Verify the request
110115
args, kwargs = fake_request.call_args
111-
assert kwargs["data"]["vehicleIds"] == [872, 1234]
116+
assert kwargs["data"]["secrets"]["DIMO_API_KEY"] == api_key
117+
assert kwargs["data"]["variables"]["USER_WALLET"] == user_wallet
118+
assert kwargs["data"]["variables"]["VEHICLE_IDS"] == vehicle_ids
112119

113120
# Verify the response
114-
assert result["vehicleIds"] == vehicle_ids
115-
assert result["mode"] == "restricted"
121+
assert result["agentId"] == "agent-def456"
116122

117-
def test_create_agent_with_empty_vehicle_list(self, monkeypatch):
118-
"""Test creating an agent with empty vehicle list (identity only)."""
123+
def test_create_agent_with_custom_personality(self, monkeypatch):
124+
"""Test creating an agent with custom personality preset."""
119125
client = DIMO(env="Dev")
120126

121127
fake_request = MagicMock(return_value={
122128
"agentId": "agent-ghi789",
123-
"user": "0xabcdef",
124-
"vehicleIds": [],
125-
"mode": "identity_only",
129+
"type": "driver_agent_v1",
130+
"personality": "helpful_assistant",
126131
"createdAt": "2024-01-01T00:00:00Z"
127132
})
128133
monkeypatch.setattr(client, "request", fake_request)
129134

130135
result = client.conversations.create_agent(
131136
developer_jwt="test_jwt",
132-
user="0xabcdef",
133-
vehicle_ids=[]
137+
api_key="0xapikey",
138+
user_wallet="0xwallet",
139+
agent_type="driver_agent_v1",
140+
personality="helpful_assistant"
134141
)
135142

136-
assert result["vehicleIds"] == []
137-
assert result["mode"] == "identity_only"
143+
# Verify the request
144+
args, kwargs = fake_request.call_args
145+
assert kwargs["data"]["personality"] == "helpful_assistant"
146+
147+
# Verify the response
148+
assert result["personality"] == "helpful_assistant"
149+
150+
def test_create_agent_full_config(self, monkeypatch):
151+
"""Test creating an agent with all configuration options."""
152+
client = DIMO(env="Dev")
153+
154+
fake_request = MagicMock(return_value={
155+
"agentId": "agent-full123",
156+
"type": "driver_agent_v1",
157+
"personality": "uncle_mechanic",
158+
"createdAt": "2024-01-01T00:00:00Z"
159+
})
160+
monkeypatch.setattr(client, "request", fake_request)
161+
162+
result = client.conversations.create_agent(
163+
developer_jwt="test_jwt",
164+
api_key="0x1234567890abcdef",
165+
user_wallet="0x86b04f6d1D9E79aD7eB31cDEAF37442B00d64605",
166+
vehicle_ids="[1, 2, 3]",
167+
agent_type="driver_agent_v1",
168+
personality="uncle_mechanic"
169+
)
170+
171+
# Verify all fields are in request
172+
args, kwargs = fake_request.call_args
173+
assert kwargs["data"]["type"] == "driver_agent_v1"
174+
assert kwargs["data"]["personality"] == "uncle_mechanic"
175+
assert kwargs["data"]["secrets"]["DIMO_API_KEY"] == "0x1234567890abcdef"
176+
assert kwargs["data"]["variables"]["USER_WALLET"] == "0x86b04f6d1D9E79aD7eB31cDEAF37442B00d64605"
177+
assert kwargs["data"]["variables"]["VEHICLE_IDS"] == "[1, 2, 3]"
138178

139179
def test_create_agent_invalid_types(self):
140180
"""Test that type checking is enforced for parameters."""
@@ -144,22 +184,56 @@ def test_create_agent_invalid_types(self):
144184
with pytest.raises(DimoTypeError):
145185
client.conversations.create_agent(
146186
developer_jwt=123, # Should be string
147-
user="0xabcdef"
187+
api_key="0xapikey",
188+
user_wallet="0xwallet",
189+
agent_type="driver_agent_v1"
190+
)
191+
192+
# Test invalid api_key type
193+
with pytest.raises(DimoTypeError):
194+
client.conversations.create_agent(
195+
developer_jwt="test_jwt",
196+
api_key=123, # Should be string
197+
user_wallet="0xwallet",
198+
agent_type="driver_agent_v1"
199+
)
200+
201+
# Test invalid user_wallet type
202+
with pytest.raises(DimoTypeError):
203+
client.conversations.create_agent(
204+
developer_jwt="test_jwt",
205+
api_key="0xapikey",
206+
user_wallet=123, # Should be string
207+
agent_type="driver_agent_v1"
148208
)
149209

150-
# Test invalid user type
210+
# Test invalid agent_type type
151211
with pytest.raises(DimoTypeError):
152212
client.conversations.create_agent(
153213
developer_jwt="test_jwt",
154-
user=123 # Should be string
214+
api_key="0xapikey",
215+
user_wallet="0xwallet",
216+
agent_type=123 # Should be string
155217
)
156218

157219
# Test invalid vehicle_ids type
158220
with pytest.raises(DimoTypeError):
159221
client.conversations.create_agent(
160222
developer_jwt="test_jwt",
161-
user="0xabcdef",
162-
vehicle_ids="not_a_list" # Should be list or None
223+
api_key="0xapikey",
224+
user_wallet="0xwallet",
225+
agent_type="driver_agent_v1",
226+
vehicle_ids=123 # Should be string or None
227+
)
228+
229+
# Test invalid personality type
230+
with pytest.raises(DimoTypeError):
231+
client.conversations.create_agent(
232+
developer_jwt="test_jwt",
233+
api_key="0xapikey",
234+
user_wallet="0xwallet",
235+
agent_type="driver_agent_v1",
236+
personality=123 # Should be string
163237
)
164238

165239

@@ -573,8 +647,8 @@ def fake_request(*args, **kwargs):
573647
if args[0] == "POST" and args[2] == "/agents":
574648
return {
575649
"agentId": "agent-test123",
576-
"user": "0xuser",
577-
"vehicleIds": [872],
650+
"type": "driver_agent_v1",
651+
"personality": "uncle_mechanic",
578652
"createdAt": "2024-01-01T00:00:00Z"
579653
}
580654
elif args[0] == "POST" and "/message" in args[2]:
@@ -592,8 +666,10 @@ def fake_request(*args, **kwargs):
592666
# 1. Create agent
593667
agent = client.conversations.create_agent(
594668
developer_jwt="test_jwt",
595-
user="0xuser",
596-
vehicle_ids=[872]
669+
api_key="0x1234567890abcdef",
670+
user_wallet="0x86b04f6d1D9E79aD7eB31cDEAF37442B00d64605",
671+
agent_type="driver_agent_v1",
672+
vehicle_ids="[872]"
597673
)
598674
assert agent["agentId"] == "agent-test123"
599675
assert ("POST", "/agents") in calls_made

0 commit comments

Comments
 (0)