karrio-cli 2025.5rc3__py3-none-any.whl
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.
- karrio_cli/__init__.py +0 -0
- karrio_cli/__main__.py +105 -0
- karrio_cli/ai/README.md +335 -0
- karrio_cli/ai/__init__.py +0 -0
- karrio_cli/ai/commands.py +102 -0
- karrio_cli/ai/karrio_ai/__init__.py +1 -0
- karrio_cli/ai/karrio_ai/agent.py +972 -0
- karrio_cli/ai/karrio_ai/architecture/INTEGRATION_AGENT_PROMPT.md +497 -0
- karrio_cli/ai/karrio_ai/architecture/MAPPING_AGENT_PROMPT.md +355 -0
- karrio_cli/ai/karrio_ai/architecture/REAL_WORLD_TESTING.md +305 -0
- karrio_cli/ai/karrio_ai/architecture/SCHEMA_AGENT_PROMPT.md +183 -0
- karrio_cli/ai/karrio_ai/architecture/TESTING_AGENT_PROMPT.md +448 -0
- karrio_cli/ai/karrio_ai/architecture/TESTING_GUIDE.md +271 -0
- karrio_cli/ai/karrio_ai/enhanced_tools.py +943 -0
- karrio_cli/ai/karrio_ai/rag_system.py +503 -0
- karrio_cli/ai/karrio_ai/tests/test_agent.py +350 -0
- karrio_cli/ai/karrio_ai/tests/test_real_integration.py +360 -0
- karrio_cli/ai/karrio_ai/tests/test_real_world_scenarios.py +513 -0
- karrio_cli/commands/__init__.py +0 -0
- karrio_cli/commands/codegen.py +336 -0
- karrio_cli/commands/login.py +139 -0
- karrio_cli/commands/plugins.py +168 -0
- karrio_cli/commands/sdk.py +870 -0
- karrio_cli/common/queries.py +101 -0
- karrio_cli/common/utils.py +368 -0
- karrio_cli/resources/__init__.py +0 -0
- karrio_cli/resources/carriers.py +91 -0
- karrio_cli/resources/connections.py +207 -0
- karrio_cli/resources/events.py +151 -0
- karrio_cli/resources/logs.py +151 -0
- karrio_cli/resources/orders.py +144 -0
- karrio_cli/resources/shipments.py +210 -0
- karrio_cli/resources/trackers.py +287 -0
- karrio_cli/templates/__init__.py +9 -0
- karrio_cli/templates/__pycache__/__init__.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/__init__.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/address.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/address.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/docs.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/docs.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/documents.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/documents.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/manifest.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/manifest.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/pickup.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/pickup.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/rates.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/rates.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/sdk.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/sdk.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/shipments.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/shipments.cpython-312.pyc +0 -0
- karrio_cli/templates/__pycache__/tracking.cpython-311.pyc +0 -0
- karrio_cli/templates/__pycache__/tracking.cpython-312.pyc +0 -0
- karrio_cli/templates/address.py +308 -0
- karrio_cli/templates/docs.py +150 -0
- karrio_cli/templates/documents.py +428 -0
- karrio_cli/templates/manifest.py +396 -0
- karrio_cli/templates/pickup.py +839 -0
- karrio_cli/templates/rates.py +638 -0
- karrio_cli/templates/sdk.py +947 -0
- karrio_cli/templates/shipments.py +892 -0
- karrio_cli/templates/tracking.py +437 -0
- karrio_cli-2025.5rc3.dist-info/METADATA +165 -0
- karrio_cli-2025.5rc3.dist-info/RECORD +68 -0
- karrio_cli-2025.5rc3.dist-info/WHEEL +5 -0
- karrio_cli-2025.5rc3.dist-info/entry_points.txt +2 -0
- karrio_cli-2025.5rc3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,972 @@
|
|
1
|
+
import os
|
2
|
+
import typing
|
3
|
+
from pathlib import Path
|
4
|
+
from google.adk.agents import Agent, SequentialAgent
|
5
|
+
from google.adk.tools import FunctionTool
|
6
|
+
from google.adk import Agent as BaseAgent
|
7
|
+
|
8
|
+
# Import the RAG system
|
9
|
+
from .rag_system import get_rag_system, search_implementation_patterns
|
10
|
+
|
11
|
+
# Get the workspace root directory - resolve relative to this file's location
|
12
|
+
WORKSPACE_ROOT = Path(__file__).resolve().parents[5] # Use resolve() for consistent path handling
|
13
|
+
CONNECTORS_DIR = WORKSPACE_ROOT / "modules" / "connectors"
|
14
|
+
SDK_DIR = WORKSPACE_ROOT / "modules" / "sdk"
|
15
|
+
CLI_TEMPLATES_DIR = WORKSPACE_ROOT / "modules" / "cli" / "karrio_cli" / "templates"
|
16
|
+
|
17
|
+
# Initialize RAG system
|
18
|
+
RAG_SYSTEM = get_rag_system(WORKSPACE_ROOT)
|
19
|
+
|
20
|
+
|
21
|
+
# Tool Functions for Connector Generation
|
22
|
+
def analyze_existing_connector(
|
23
|
+
carrier_name: str,
|
24
|
+
analysis_type: str = "structure"
|
25
|
+
) -> typing.Dict[str, typing.Any]:
|
26
|
+
"""
|
27
|
+
Analyze an existing connector to understand patterns and structure using RAG system.
|
28
|
+
|
29
|
+
Args:
|
30
|
+
carrier_name: Name of the carrier (e.g., 'ups', 'fedex', 'canadapost')
|
31
|
+
analysis_type: Type of analysis ('structure', 'mappings', 'schemas', 'tests', 'all')
|
32
|
+
|
33
|
+
Returns:
|
34
|
+
Dictionary containing comprehensive analysis results with patterns
|
35
|
+
"""
|
36
|
+
connector_path = CONNECTORS_DIR / carrier_name
|
37
|
+
|
38
|
+
if not connector_path.exists():
|
39
|
+
return {"error": f"Connector {carrier_name} not found"}
|
40
|
+
|
41
|
+
# Use RAG system for comprehensive analysis
|
42
|
+
analysis = RAG_SYSTEM.analyze_carrier_structure(carrier_name)
|
43
|
+
analysis["analysis_type"] = analysis_type
|
44
|
+
analysis["path"] = str(connector_path)
|
45
|
+
|
46
|
+
try:
|
47
|
+
# Add traditional directory analysis if requested
|
48
|
+
if analysis_type in ["structure", "all"]:
|
49
|
+
analysis["directory_structure"] = _analyze_directory_structure(connector_path)
|
50
|
+
|
51
|
+
# Get patterns from RAG system
|
52
|
+
if analysis_type in ["mappings", "all"]:
|
53
|
+
mapping_patterns = RAG_SYSTEM.search_patterns(
|
54
|
+
pattern_type="mapping",
|
55
|
+
carrier_name=carrier_name
|
56
|
+
)
|
57
|
+
analysis["mapping_patterns"] = [
|
58
|
+
{
|
59
|
+
"description": p.description,
|
60
|
+
"code_example": p.code_example[:500], # Truncate for brevity
|
61
|
+
"confidence": p.confidence_score
|
62
|
+
}
|
63
|
+
for p in mapping_patterns
|
64
|
+
]
|
65
|
+
|
66
|
+
if analysis_type in ["schemas", "all"]:
|
67
|
+
schema_patterns = RAG_SYSTEM.search_patterns(
|
68
|
+
pattern_type="schema",
|
69
|
+
carrier_name=carrier_name
|
70
|
+
)
|
71
|
+
analysis["schema_patterns"] = [
|
72
|
+
{
|
73
|
+
"description": p.description,
|
74
|
+
"code_example": p.code_example[:500],
|
75
|
+
"confidence": p.confidence_score
|
76
|
+
}
|
77
|
+
for p in schema_patterns
|
78
|
+
]
|
79
|
+
|
80
|
+
if analysis_type in ["auth", "all"]:
|
81
|
+
auth_patterns = RAG_SYSTEM.search_patterns(
|
82
|
+
pattern_type="auth",
|
83
|
+
carrier_name=carrier_name
|
84
|
+
)
|
85
|
+
analysis["auth_patterns"] = [
|
86
|
+
{
|
87
|
+
"description": p.description,
|
88
|
+
"code_example": p.code_example[:500],
|
89
|
+
"confidence": p.confidence_score
|
90
|
+
}
|
91
|
+
for p in auth_patterns
|
92
|
+
]
|
93
|
+
|
94
|
+
# Add implementation examples
|
95
|
+
if analysis_type in ["examples", "all"]:
|
96
|
+
rate_examples = RAG_SYSTEM.get_implementation_examples("rate", [carrier_name])
|
97
|
+
shipment_examples = RAG_SYSTEM.get_implementation_examples("shipment", [carrier_name])
|
98
|
+
|
99
|
+
analysis["implementation_examples"] = {
|
100
|
+
"rate_operations": [
|
101
|
+
{
|
102
|
+
"file": ex.file_path,
|
103
|
+
"function": ex.content.split('\n')[0] if ex.content else "",
|
104
|
+
"lines": f"{ex.start_line}-{ex.end_line}"
|
105
|
+
}
|
106
|
+
for ex in rate_examples[:3]
|
107
|
+
],
|
108
|
+
"shipment_operations": [
|
109
|
+
{
|
110
|
+
"file": ex.file_path,
|
111
|
+
"function": ex.content.split('\n')[0] if ex.content else "",
|
112
|
+
"lines": f"{ex.start_line}-{ex.end_line}"
|
113
|
+
}
|
114
|
+
for ex in shipment_examples[:3]
|
115
|
+
]
|
116
|
+
}
|
117
|
+
|
118
|
+
except Exception as e:
|
119
|
+
analysis["error"] = str(e)
|
120
|
+
|
121
|
+
return analysis
|
122
|
+
|
123
|
+
|
124
|
+
def extract_carrier_patterns(
|
125
|
+
similar_carriers: typing.Optional[typing.List[str]] = None,
|
126
|
+
pattern_type: str = "api_structure"
|
127
|
+
) -> typing.Dict[str, typing.Any]:
|
128
|
+
"""
|
129
|
+
Extract common patterns from similar carriers using RAG system for new integration.
|
130
|
+
|
131
|
+
Args:
|
132
|
+
similar_carriers: List of similar carriers to analyze
|
133
|
+
pattern_type: Type of patterns to extract ('auth', 'mapping', 'schema', 'error_handling', 'all')
|
134
|
+
|
135
|
+
Returns:
|
136
|
+
Dictionary containing extracted patterns with code examples and similarities
|
137
|
+
"""
|
138
|
+
if similar_carriers is None:
|
139
|
+
# Use RAG system to find default similar carriers
|
140
|
+
similar_carriers = ["ups", "fedex", "canadapost", "dhl_express", "usps"]
|
141
|
+
|
142
|
+
patterns = {
|
143
|
+
"pattern_type": pattern_type,
|
144
|
+
"analyzed_carriers": similar_carriers,
|
145
|
+
"common_patterns": {},
|
146
|
+
"variations": {},
|
147
|
+
"code_examples": {},
|
148
|
+
"best_practices": [],
|
149
|
+
"analysis": {}
|
150
|
+
}
|
151
|
+
|
152
|
+
try:
|
153
|
+
# Extract patterns using RAG system
|
154
|
+
all_patterns = []
|
155
|
+
|
156
|
+
for carrier in similar_carriers:
|
157
|
+
if pattern_type == "all":
|
158
|
+
# Get all pattern types for comprehensive analysis
|
159
|
+
carrier_patterns = {
|
160
|
+
"auth": RAG_SYSTEM.search_patterns("auth", carrier_name=carrier, limit=3),
|
161
|
+
"mapping": RAG_SYSTEM.search_patterns("mapping", carrier_name=carrier, limit=3),
|
162
|
+
"schema": RAG_SYSTEM.search_patterns("schema", carrier_name=carrier, limit=3),
|
163
|
+
"error_handling": RAG_SYSTEM.search_patterns("error_handling", carrier_name=carrier, limit=3)
|
164
|
+
}
|
165
|
+
else:
|
166
|
+
carrier_patterns = RAG_SYSTEM.search_patterns(pattern_type, carrier_name=carrier, limit=5)
|
167
|
+
|
168
|
+
patterns["common_patterns"][carrier] = carrier_patterns
|
169
|
+
|
170
|
+
# Collect all patterns for similarity analysis
|
171
|
+
if isinstance(carrier_patterns, dict):
|
172
|
+
for pt, pt_patterns in carrier_patterns.items():
|
173
|
+
all_patterns.extend(pt_patterns)
|
174
|
+
else:
|
175
|
+
all_patterns.extend(carrier_patterns)
|
176
|
+
|
177
|
+
# Analyze pattern similarities and extract common approaches
|
178
|
+
patterns["similarities"] = _analyze_pattern_similarities(all_patterns)
|
179
|
+
|
180
|
+
# Extract best practices based on confidence scores
|
181
|
+
high_confidence_patterns = [p for p in all_patterns if p.confidence_score > 0.6]
|
182
|
+
patterns["best_practices"] = [
|
183
|
+
{
|
184
|
+
"description": p.description,
|
185
|
+
"carrier": p.carrier_name,
|
186
|
+
"code_snippet": p.code_example[:300],
|
187
|
+
"confidence": p.confidence_score
|
188
|
+
}
|
189
|
+
for p in sorted(high_confidence_patterns, key=lambda x: x.confidence_score, reverse=True)[:10]
|
190
|
+
]
|
191
|
+
|
192
|
+
# Generate code examples for common patterns
|
193
|
+
if pattern_type in ["mapping", "all"]:
|
194
|
+
patterns["code_examples"]["rate_mapping"] = _extract_rate_mapping_examples(similar_carriers)
|
195
|
+
patterns["code_examples"]["shipment_mapping"] = _extract_shipment_mapping_examples(similar_carriers)
|
196
|
+
|
197
|
+
if pattern_type in ["auth", "all"]:
|
198
|
+
patterns["code_examples"]["authentication"] = _extract_auth_examples(similar_carriers)
|
199
|
+
|
200
|
+
if pattern_type in ["schema", "all"]:
|
201
|
+
patterns["code_examples"]["schema_definitions"] = _extract_schema_examples(similar_carriers)
|
202
|
+
|
203
|
+
# Add comprehensive analysis
|
204
|
+
patterns["analysis"] = {
|
205
|
+
"total_patterns_found": len(all_patterns),
|
206
|
+
"carriers_with_patterns": len(set(p.carrier_name for p in all_patterns)),
|
207
|
+
"most_common_pattern_types": [pt for pt, count in
|
208
|
+
sorted([(pt, sum(1 for p in all_patterns if p.pattern_type == pt))
|
209
|
+
for pt in set(p.pattern_type for p in all_patterns)],
|
210
|
+
key=lambda x: x[1], reverse=True)[:3]],
|
211
|
+
"avg_confidence": sum(p.confidence_score for p in all_patterns) / len(all_patterns) if all_patterns else 0
|
212
|
+
}
|
213
|
+
|
214
|
+
except Exception as e:
|
215
|
+
patterns["error"] = str(e)
|
216
|
+
|
217
|
+
return patterns
|
218
|
+
|
219
|
+
|
220
|
+
def generate_carrier_schema(
|
221
|
+
carrier_name: str,
|
222
|
+
api_documentation: str,
|
223
|
+
schema_type: str = "complete"
|
224
|
+
) -> typing.Dict[str, typing.Any]:
|
225
|
+
"""
|
226
|
+
Generate Python schemas from carrier API documentation.
|
227
|
+
|
228
|
+
Args:
|
229
|
+
carrier_name: Name of the new carrier
|
230
|
+
api_documentation: API documentation or JSON schema
|
231
|
+
schema_type: Type of schema generation ('rates', 'shipments', 'tracking', 'complete')
|
232
|
+
|
233
|
+
Returns:
|
234
|
+
Dictionary containing generated schema code
|
235
|
+
"""
|
236
|
+
result = {
|
237
|
+
"carrier": carrier_name,
|
238
|
+
"schema_type": schema_type,
|
239
|
+
"generated_schemas": {},
|
240
|
+
"imports": [],
|
241
|
+
"classes": []
|
242
|
+
}
|
243
|
+
|
244
|
+
try:
|
245
|
+
# Try to import codegen, but handle gracefully if not available
|
246
|
+
try:
|
247
|
+
from karrio_cli.commands.codegen import transform_content
|
248
|
+
codegen_available = True
|
249
|
+
except ImportError:
|
250
|
+
codegen_available = False
|
251
|
+
|
252
|
+
# Use existing codegen transform functionality if available
|
253
|
+
# Temporarily disable codegen until we can debug the integration properly
|
254
|
+
use_codegen = False # Set to False to always use fallback
|
255
|
+
|
256
|
+
if use_codegen and codegen_available and api_documentation.strip().startswith('{'):
|
257
|
+
# Assume JSON schema input
|
258
|
+
# This would integrate with existing quicktype/codegen pipeline
|
259
|
+
transformed = transform_content(api_documentation, append_type_suffix=True)
|
260
|
+
result["generated_schemas"][schema_type] = transformed
|
261
|
+
result["classes"] = _extract_class_names(transformed)
|
262
|
+
else:
|
263
|
+
# Enhanced fallback: Generate comprehensive schema structure
|
264
|
+
schemas_generated = []
|
265
|
+
|
266
|
+
if schema_type in ["rates", "complete"]:
|
267
|
+
rate_schema = _generate_rate_schema(carrier_name)
|
268
|
+
schemas_generated.extend(rate_schema["classes"])
|
269
|
+
result["generated_schemas"]["rates"] = rate_schema["code"]
|
270
|
+
|
271
|
+
if schema_type in ["shipments", "complete"]:
|
272
|
+
shipment_schema = _generate_shipment_schema(carrier_name)
|
273
|
+
schemas_generated.extend(shipment_schema["classes"])
|
274
|
+
result["generated_schemas"]["shipments"] = shipment_schema["code"]
|
275
|
+
|
276
|
+
if schema_type in ["tracking", "complete"]:
|
277
|
+
tracking_schema = _generate_tracking_schema(carrier_name)
|
278
|
+
schemas_generated.extend(tracking_schema["classes"])
|
279
|
+
result["generated_schemas"]["tracking"] = tracking_schema["code"]
|
280
|
+
|
281
|
+
# If specific schema type, generate that
|
282
|
+
if schema_type not in ["rates", "shipments", "tracking", "complete"]:
|
283
|
+
basic_schema = _generate_basic_schema(carrier_name, schema_type)
|
284
|
+
schemas_generated.extend(basic_schema["classes"])
|
285
|
+
result["generated_schemas"][schema_type] = basic_schema["code"]
|
286
|
+
|
287
|
+
result["classes"] = schemas_generated
|
288
|
+
|
289
|
+
# Generate appropriate imports
|
290
|
+
result["imports"] = [
|
291
|
+
"import attr",
|
292
|
+
"import jstruct",
|
293
|
+
"import typing",
|
294
|
+
"from karrio.core.models import *",
|
295
|
+
"from karrio.core.utils import *"
|
296
|
+
]
|
297
|
+
|
298
|
+
except Exception as e:
|
299
|
+
result["error"] = str(e)
|
300
|
+
|
301
|
+
return result
|
302
|
+
|
303
|
+
|
304
|
+
def generate_carrier_mappings(
|
305
|
+
carrier_name: str,
|
306
|
+
api_endpoints: typing.Dict[str, str],
|
307
|
+
operation_type: str = "complete"
|
308
|
+
) -> typing.Dict[str, typing.Any]:
|
309
|
+
"""
|
310
|
+
Generate mapping files for carrier API operations.
|
311
|
+
|
312
|
+
Args:
|
313
|
+
carrier_name: Name of the carrier
|
314
|
+
api_endpoints: Dictionary of API endpoints
|
315
|
+
operation_type: Type of operations ('rates', 'shipments', 'tracking', 'complete')
|
316
|
+
|
317
|
+
Returns:
|
318
|
+
Dictionary containing generated mapping code
|
319
|
+
"""
|
320
|
+
result = {
|
321
|
+
"carrier": carrier_name,
|
322
|
+
"operation_type": operation_type,
|
323
|
+
"generated_mappings": {},
|
324
|
+
"endpoints": api_endpoints
|
325
|
+
}
|
326
|
+
|
327
|
+
try:
|
328
|
+
# Generate mappings based on existing templates
|
329
|
+
templates_path = CLI_TEMPLATES_DIR
|
330
|
+
|
331
|
+
if operation_type in ["rates", "complete"]:
|
332
|
+
result["generated_mappings"]["rates"] = _generate_rates_mapping(
|
333
|
+
carrier_name, api_endpoints.get("rates", ""), templates_path
|
334
|
+
)
|
335
|
+
|
336
|
+
if operation_type in ["shipments", "complete"]:
|
337
|
+
result["generated_mappings"]["shipments"] = _generate_shipments_mapping(
|
338
|
+
carrier_name, api_endpoints.get("shipments", ""), templates_path
|
339
|
+
)
|
340
|
+
|
341
|
+
if operation_type in ["tracking", "complete"]:
|
342
|
+
result["generated_mappings"]["tracking"] = _generate_tracking_mapping(
|
343
|
+
carrier_name, api_endpoints.get("tracking", ""), templates_path
|
344
|
+
)
|
345
|
+
|
346
|
+
except Exception as e:
|
347
|
+
result["error"] = str(e)
|
348
|
+
|
349
|
+
return result
|
350
|
+
|
351
|
+
|
352
|
+
def generate_integration_tests(
|
353
|
+
carrier_name: str,
|
354
|
+
test_data: typing.Dict[str, typing.Any],
|
355
|
+
test_type: str = "complete"
|
356
|
+
) -> typing.Dict[str, typing.Any]:
|
357
|
+
"""
|
358
|
+
Generate comprehensive tests for carrier integration.
|
359
|
+
|
360
|
+
Args:
|
361
|
+
carrier_name: Name of the carrier
|
362
|
+
test_data: Test data and configurations
|
363
|
+
test_type: Type of tests ('unit', 'integration', 'complete')
|
364
|
+
|
365
|
+
Returns:
|
366
|
+
Dictionary containing generated test code
|
367
|
+
"""
|
368
|
+
result = {
|
369
|
+
"carrier": carrier_name,
|
370
|
+
"test_type": test_type,
|
371
|
+
"generated_tests": {},
|
372
|
+
"test_files": []
|
373
|
+
}
|
374
|
+
|
375
|
+
try:
|
376
|
+
# Generate different types of tests
|
377
|
+
if test_type in ["unit", "complete"]:
|
378
|
+
result["generated_tests"]["unit"] = _generate_unit_tests(
|
379
|
+
carrier_name, test_data
|
380
|
+
)
|
381
|
+
|
382
|
+
if test_type in ["integration", "complete"]:
|
383
|
+
result["generated_tests"]["integration"] = _generate_integration_tests(
|
384
|
+
carrier_name, test_data
|
385
|
+
)
|
386
|
+
|
387
|
+
# Generate test fixtures
|
388
|
+
result["generated_tests"]["fixtures"] = _generate_test_fixtures(
|
389
|
+
carrier_name, test_data
|
390
|
+
)
|
391
|
+
|
392
|
+
except Exception as e:
|
393
|
+
result["error"] = str(e)
|
394
|
+
|
395
|
+
return result
|
396
|
+
|
397
|
+
|
398
|
+
def assemble_complete_integration(
|
399
|
+
carrier_name: str,
|
400
|
+
integration_config: typing.Dict[str, typing.Any]
|
401
|
+
) -> typing.Dict[str, typing.Any]:
|
402
|
+
"""
|
403
|
+
Assemble a complete carrier integration with all components using official Karrio tools.
|
404
|
+
|
405
|
+
Args:
|
406
|
+
carrier_name: Name of the carrier
|
407
|
+
integration_config: Complete integration configuration
|
408
|
+
|
409
|
+
Returns:
|
410
|
+
Dictionary containing the complete integration structure with proper instructions
|
411
|
+
"""
|
412
|
+
from .enhanced_tools import create_karrio_plugin_structure, get_karrio_plugin_structure_info
|
413
|
+
|
414
|
+
result = {
|
415
|
+
"carrier": carrier_name,
|
416
|
+
"integration_complete": False,
|
417
|
+
"official_structure": True,
|
418
|
+
"generation_method": "kcli_sdk_tools",
|
419
|
+
"next_steps": []
|
420
|
+
}
|
421
|
+
|
422
|
+
try:
|
423
|
+
# Get carrier slug from config or generate it
|
424
|
+
carrier_slug = integration_config.get("carrier_slug",
|
425
|
+
carrier_name.lower().replace(" ", "_").replace("-", "_"))
|
426
|
+
|
427
|
+
# Determine features and API type from config
|
428
|
+
features = integration_config.get("features", "rating,shipping,tracking")
|
429
|
+
is_xml_api = integration_config.get("is_xml_api", False)
|
430
|
+
output_path = integration_config.get("output_path", "./plugins")
|
431
|
+
|
432
|
+
# Generate plugin structure using official tools
|
433
|
+
plugin_result = create_karrio_plugin_structure(
|
434
|
+
carrier_slug=carrier_slug,
|
435
|
+
carrier_name=carrier_name,
|
436
|
+
features=features,
|
437
|
+
is_xml_api=is_xml_api,
|
438
|
+
output_path=output_path
|
439
|
+
)
|
440
|
+
|
441
|
+
# Get structure information
|
442
|
+
structure_info = get_karrio_plugin_structure_info()
|
443
|
+
|
444
|
+
result.update({
|
445
|
+
"plugin_generation": plugin_result,
|
446
|
+
"structure_info": structure_info,
|
447
|
+
"integration_complete": plugin_result["success"],
|
448
|
+
"carrier_slug": carrier_slug,
|
449
|
+
"features": features,
|
450
|
+
"is_xml_api": is_xml_api,
|
451
|
+
})
|
452
|
+
|
453
|
+
if plugin_result["success"]:
|
454
|
+
result["next_steps"] = [
|
455
|
+
"🚀 OFFICIAL KARRIO PLUGIN GENERATION:",
|
456
|
+
"",
|
457
|
+
"1. **Generate Plugin Structure:**",
|
458
|
+
f" Run: {plugin_result['manual_command']}",
|
459
|
+
"",
|
460
|
+
"2. **When prompted, enter:**",
|
461
|
+
f" • Carrier slug: {carrier_slug}",
|
462
|
+
f" • Display name: {carrier_name}",
|
463
|
+
f" • Features: {features}",
|
464
|
+
" • Version: 2025.1",
|
465
|
+
f" • Is XML API: {'Yes' if is_xml_api else 'No'}",
|
466
|
+
"",
|
467
|
+
"3. **Implementation Steps:**",
|
468
|
+
" • Update schema files with actual API specifications",
|
469
|
+
" • Run ./generate to create Python dataclasses",
|
470
|
+
" • Implement provider files (rates.py, shipments.py, tracking.py)",
|
471
|
+
" • Configure utils.py with carrier settings and authentication",
|
472
|
+
" • Implement error.py for error handling",
|
473
|
+
" • Update units.py with carrier-specific enums",
|
474
|
+
" • Implement proxy.py for API communication",
|
475
|
+
" • Create comprehensive tests",
|
476
|
+
"",
|
477
|
+
"4. **File Structure Created:**",
|
478
|
+
] + [f" {file}" for file in plugin_result["generated_structure"]["files"][:10]]
|
479
|
+
|
480
|
+
if len(plugin_result["generated_structure"]["files"]) > 10:
|
481
|
+
result["next_steps"].append(f" ... and {len(plugin_result['generated_structure']['files']) - 10} more files")
|
482
|
+
|
483
|
+
except Exception as e:
|
484
|
+
result["error"] = str(e)
|
485
|
+
result["integration_complete"] = False
|
486
|
+
|
487
|
+
return result
|
488
|
+
|
489
|
+
|
490
|
+
# Helper functions (simplified for brevity)
|
491
|
+
def _analyze_directory_structure(path: Path) -> typing.Dict[str, typing.Any]:
|
492
|
+
"""Analyze directory structure of a connector."""
|
493
|
+
structure = {"directories": [], "files": []}
|
494
|
+
try:
|
495
|
+
for item in path.rglob("*"):
|
496
|
+
if item.is_dir():
|
497
|
+
structure["directories"].append(str(item.relative_to(path)))
|
498
|
+
else:
|
499
|
+
structure["files"].append(str(item.relative_to(path)))
|
500
|
+
except Exception:
|
501
|
+
pass
|
502
|
+
return structure
|
503
|
+
|
504
|
+
|
505
|
+
def _analyze_mapping_files(path: Path) -> typing.List[str]:
|
506
|
+
"""Analyze mapping files in a connector."""
|
507
|
+
mappings = []
|
508
|
+
try:
|
509
|
+
for py_file in path.glob("*.py"):
|
510
|
+
mappings.append(py_file.name)
|
511
|
+
except Exception:
|
512
|
+
pass
|
513
|
+
return mappings
|
514
|
+
|
515
|
+
|
516
|
+
def _analyze_schema_files(path: Path) -> typing.List[str]:
|
517
|
+
"""Analyze schema files in a connector."""
|
518
|
+
schemas = []
|
519
|
+
try:
|
520
|
+
for py_file in path.rglob("*.py"):
|
521
|
+
schemas.append(str(py_file.relative_to(path)))
|
522
|
+
except Exception:
|
523
|
+
pass
|
524
|
+
return schemas
|
525
|
+
|
526
|
+
|
527
|
+
def _analyze_test_files(path: Path) -> typing.List[str]:
|
528
|
+
"""Analyze test files in a connector."""
|
529
|
+
tests = []
|
530
|
+
try:
|
531
|
+
for py_file in path.glob("*.py"):
|
532
|
+
tests.append(py_file.name)
|
533
|
+
except Exception:
|
534
|
+
pass
|
535
|
+
return tests
|
536
|
+
|
537
|
+
|
538
|
+
def _extract_class_names(content: str) -> typing.List[str]:
|
539
|
+
"""Extract class names from Python code."""
|
540
|
+
import re
|
541
|
+
return re.findall(r"class\s+(\w+)", content)
|
542
|
+
|
543
|
+
|
544
|
+
def _generate_rates_mapping(carrier_name: str, endpoint: str, templates_path: Path) -> str:
|
545
|
+
"""Generate rates mapping code."""
|
546
|
+
# This would use the existing rates.py template
|
547
|
+
return f"# Generated rates mapping for {carrier_name}\n# Endpoint: {endpoint}"
|
548
|
+
|
549
|
+
|
550
|
+
def _generate_shipments_mapping(carrier_name: str, endpoint: str, templates_path: Path) -> str:
|
551
|
+
"""Generate shipments mapping code."""
|
552
|
+
# This would use the existing shipments.py template
|
553
|
+
return f"# Generated shipments mapping for {carrier_name}\n# Endpoint: {endpoint}"
|
554
|
+
|
555
|
+
|
556
|
+
def _generate_tracking_mapping(carrier_name: str, endpoint: str, templates_path: Path) -> str:
|
557
|
+
"""Generate tracking mapping code."""
|
558
|
+
# This would use the existing tracking.py template
|
559
|
+
return f"# Generated tracking mapping for {carrier_name}\n# Endpoint: {endpoint}"
|
560
|
+
|
561
|
+
|
562
|
+
def _generate_unit_tests(carrier_name: str, test_data: typing.Dict[str, typing.Any]) -> str:
|
563
|
+
"""Generate unit tests."""
|
564
|
+
return f"# Generated unit tests for {carrier_name}"
|
565
|
+
|
566
|
+
|
567
|
+
def _generate_integration_tests(carrier_name: str, test_data: typing.Dict[str, typing.Any]) -> str:
|
568
|
+
"""Generate integration tests."""
|
569
|
+
return f"# Generated integration tests for {carrier_name}"
|
570
|
+
|
571
|
+
|
572
|
+
def _generate_test_fixtures(carrier_name: str, test_data: typing.Dict[str, typing.Any]) -> str:
|
573
|
+
"""Generate test fixtures."""
|
574
|
+
return f"# Generated test fixtures for {carrier_name}"
|
575
|
+
|
576
|
+
|
577
|
+
def _generate_project_structure(carrier_name: str) -> typing.Dict[str, typing.Any]:
|
578
|
+
"""Generate complete project structure."""
|
579
|
+
return {
|
580
|
+
"root": f"modules/connectors/{carrier_name}",
|
581
|
+
"directories": [
|
582
|
+
"karrio/providers",
|
583
|
+
"karrio/mappers",
|
584
|
+
"karrio/schemas",
|
585
|
+
"tests",
|
586
|
+
"schemas"
|
587
|
+
],
|
588
|
+
"files": [
|
589
|
+
"pyproject.toml",
|
590
|
+
"README.md",
|
591
|
+
"generate"
|
592
|
+
]
|
593
|
+
}
|
594
|
+
|
595
|
+
|
596
|
+
def _generate_file(carrier_name: str, file_type: str, config: typing.Dict[str, typing.Any]) -> str:
|
597
|
+
"""Generate individual file content."""
|
598
|
+
return f"# Generated {file_type} for {carrier_name}"
|
599
|
+
|
600
|
+
|
601
|
+
def _analyze_pattern_similarities(patterns: typing.List) -> typing.Dict[str, typing.Any]:
|
602
|
+
"""Analyze similarities between patterns from different carriers."""
|
603
|
+
if not patterns:
|
604
|
+
return {"common_approaches": [], "unique_implementations": []}
|
605
|
+
|
606
|
+
# Simple analysis based on common keywords
|
607
|
+
common_keywords = {}
|
608
|
+
for pattern in patterns:
|
609
|
+
if hasattr(pattern, 'code_example'):
|
610
|
+
words = pattern.code_example.lower().split()
|
611
|
+
for word in words:
|
612
|
+
if len(word) > 3 and word.isalpha(): # Filter meaningful words
|
613
|
+
common_keywords[word] = common_keywords.get(word, 0) + 1
|
614
|
+
|
615
|
+
# Find most common approaches
|
616
|
+
sorted_keywords = sorted(common_keywords.items(), key=lambda x: x[1], reverse=True)
|
617
|
+
|
618
|
+
return {
|
619
|
+
"common_approaches": [kw for kw, count in sorted_keywords[:10] if count > 1],
|
620
|
+
"pattern_frequency": dict(sorted_keywords[:20]),
|
621
|
+
"total_patterns_analyzed": len(patterns)
|
622
|
+
}
|
623
|
+
|
624
|
+
|
625
|
+
def _extract_rate_mapping_examples(carriers: typing.List[str]) -> typing.List[typing.Dict[str, str]]:
|
626
|
+
"""Extract rate mapping examples from carriers."""
|
627
|
+
examples = []
|
628
|
+
for carrier in carriers:
|
629
|
+
rate_examples = RAG_SYSTEM.get_implementation_examples("parse_rate_response", [carrier])
|
630
|
+
for ex in rate_examples[:2]: # Limit to 2 per carrier
|
631
|
+
examples.append({
|
632
|
+
"carrier": carrier,
|
633
|
+
"function_name": ex.content.split('\n')[0] if ex.content else "",
|
634
|
+
"file_path": ex.file_path,
|
635
|
+
"code_snippet": ex.content[:400]
|
636
|
+
})
|
637
|
+
return examples
|
638
|
+
|
639
|
+
|
640
|
+
def _extract_shipment_mapping_examples(carriers: typing.List[str]) -> typing.List[typing.Dict[str, str]]:
|
641
|
+
"""Extract shipment mapping examples from carriers."""
|
642
|
+
examples = []
|
643
|
+
for carrier in carriers:
|
644
|
+
shipment_examples = RAG_SYSTEM.get_implementation_examples("shipment_request", [carrier])
|
645
|
+
for ex in shipment_examples[:2]:
|
646
|
+
examples.append({
|
647
|
+
"carrier": carrier,
|
648
|
+
"function_name": ex.content.split('\n')[0] if ex.content else "",
|
649
|
+
"file_path": ex.file_path,
|
650
|
+
"code_snippet": ex.content[:400]
|
651
|
+
})
|
652
|
+
return examples
|
653
|
+
|
654
|
+
|
655
|
+
def _extract_auth_examples(carriers: typing.List[str]) -> typing.List[typing.Dict[str, str]]:
|
656
|
+
"""Extract authentication examples from carriers."""
|
657
|
+
examples = []
|
658
|
+
for carrier in carriers:
|
659
|
+
auth_patterns = RAG_SYSTEM.search_patterns("auth", carrier_name=carrier, limit=2)
|
660
|
+
for pattern in auth_patterns:
|
661
|
+
examples.append({
|
662
|
+
"carrier": carrier,
|
663
|
+
"description": pattern.description,
|
664
|
+
"code_snippet": pattern.code_example[:400],
|
665
|
+
"confidence": pattern.confidence_score
|
666
|
+
})
|
667
|
+
return examples
|
668
|
+
|
669
|
+
|
670
|
+
def _extract_schema_examples(carriers: typing.List[str]) -> typing.List[typing.Dict[str, str]]:
|
671
|
+
"""Extract schema definition examples from carriers."""
|
672
|
+
examples = []
|
673
|
+
for carrier in carriers:
|
674
|
+
schema_patterns = RAG_SYSTEM.search_patterns("schema", carrier_name=carrier, limit=2)
|
675
|
+
for pattern in schema_patterns:
|
676
|
+
examples.append({
|
677
|
+
"carrier": carrier,
|
678
|
+
"description": pattern.description,
|
679
|
+
"code_snippet": pattern.code_example[:400],
|
680
|
+
"confidence": pattern.confidence_score
|
681
|
+
})
|
682
|
+
return examples
|
683
|
+
|
684
|
+
|
685
|
+
def _generate_rate_schema(carrier_name: str) -> typing.Dict[str, typing.Any]:
|
686
|
+
"""Generate rate-specific schemas."""
|
687
|
+
code = f"""# Generated rate schemas for {carrier_name}
|
688
|
+
import attr
|
689
|
+
import jstruct
|
690
|
+
import typing
|
691
|
+
|
692
|
+
@attr.s(auto_attribs=True)
|
693
|
+
class {carrier_name.title()}RateRequestType:
|
694
|
+
\"\"\"Rate request schema for {carrier_name}.\"\"\"
|
695
|
+
origin: typing.Optional[str] = None
|
696
|
+
destination: typing.Optional[str] = None
|
697
|
+
packages: typing.List[typing.Dict[str, typing.Any]] = jstruct.JList[dict]
|
698
|
+
services: typing.Optional[typing.List[str]] = jstruct.JList[str]
|
699
|
+
|
700
|
+
@attr.s(auto_attribs=True)
|
701
|
+
class {carrier_name.title()}RateType:
|
702
|
+
\"\"\"Individual rate schema for {carrier_name}.\"\"\"
|
703
|
+
service_code: typing.Optional[str] = None
|
704
|
+
service_name: typing.Optional[str] = None
|
705
|
+
total_cost: typing.Optional[float] = None
|
706
|
+
currency: typing.Optional[str] = None
|
707
|
+
delivery_days: typing.Optional[int] = None
|
708
|
+
|
709
|
+
@attr.s(auto_attribs=True)
|
710
|
+
class {carrier_name.title()}RateResponseType:
|
711
|
+
\"\"\"Rate response schema for {carrier_name}.\"\"\"
|
712
|
+
rates: typing.List[{carrier_name.title()}RateType] = jstruct.JList[{carrier_name.title()}RateType]
|
713
|
+
status: typing.Optional[str] = None
|
714
|
+
"""
|
715
|
+
|
716
|
+
classes = [
|
717
|
+
f"{carrier_name.title()}RateRequestType",
|
718
|
+
f"{carrier_name.title()}RateType",
|
719
|
+
f"{carrier_name.title()}RateResponseType"
|
720
|
+
]
|
721
|
+
|
722
|
+
return {"code": code, "classes": classes}
|
723
|
+
|
724
|
+
|
725
|
+
def _generate_shipment_schema(carrier_name: str) -> typing.Dict[str, typing.Any]:
|
726
|
+
"""Generate shipment-specific schemas."""
|
727
|
+
code = f"""# Generated shipment schemas for {carrier_name}
|
728
|
+
import attr
|
729
|
+
import jstruct
|
730
|
+
import typing
|
731
|
+
|
732
|
+
@attr.s(auto_attribs=True)
|
733
|
+
class {carrier_name.title()}ShipmentRequestType:
|
734
|
+
\"\"\"Shipment request schema for {carrier_name}.\"\"\"
|
735
|
+
shipper: typing.Optional[typing.Dict[str, typing.Any]] = None
|
736
|
+
recipient: typing.Optional[typing.Dict[str, typing.Any]] = None
|
737
|
+
packages: typing.List[typing.Dict[str, typing.Any]] = jstruct.JList[dict]
|
738
|
+
service: typing.Optional[str] = None
|
739
|
+
|
740
|
+
@attr.s(auto_attribs=True)
|
741
|
+
class {carrier_name.title()}ShipmentResponseType:
|
742
|
+
\"\"\"Shipment response schema for {carrier_name}.\"\"\"
|
743
|
+
tracking_number: typing.Optional[str] = None
|
744
|
+
label_url: typing.Optional[str] = None
|
745
|
+
shipment_id: typing.Optional[str] = None
|
746
|
+
status: typing.Optional[str] = None
|
747
|
+
"""
|
748
|
+
|
749
|
+
classes = [
|
750
|
+
f"{carrier_name.title()}ShipmentRequestType",
|
751
|
+
f"{carrier_name.title()}ShipmentResponseType"
|
752
|
+
]
|
753
|
+
|
754
|
+
return {"code": code, "classes": classes}
|
755
|
+
|
756
|
+
|
757
|
+
def _generate_tracking_schema(carrier_name: str) -> typing.Dict[str, typing.Any]:
|
758
|
+
"""Generate tracking-specific schemas."""
|
759
|
+
code = f"""# Generated tracking schemas for {carrier_name}
|
760
|
+
import attr
|
761
|
+
import jstruct
|
762
|
+
import typing
|
763
|
+
|
764
|
+
@attr.s(auto_attribs=True)
|
765
|
+
class {carrier_name.title()}TrackingRequestType:
|
766
|
+
\"\"\"Tracking request schema for {carrier_name}.\"\"\"
|
767
|
+
tracking_numbers: typing.List[str] = jstruct.JList[str]
|
768
|
+
|
769
|
+
@attr.s(auto_attribs=True)
|
770
|
+
class {carrier_name.title()}TrackingEventType:
|
771
|
+
\"\"\"Tracking event schema for {carrier_name}.\"\"\"
|
772
|
+
date: typing.Optional[str] = None
|
773
|
+
time: typing.Optional[str] = None
|
774
|
+
location: typing.Optional[str] = None
|
775
|
+
status: typing.Optional[str] = None
|
776
|
+
description: typing.Optional[str] = None
|
777
|
+
|
778
|
+
@attr.s(auto_attribs=True)
|
779
|
+
class {carrier_name.title()}TrackingResponseType:
|
780
|
+
\"\"\"Tracking response schema for {carrier_name}.\"\"\"
|
781
|
+
tracking_number: typing.Optional[str] = None
|
782
|
+
status: typing.Optional[str] = None
|
783
|
+
events: typing.List[{carrier_name.title()}TrackingEventType] = jstruct.JList[{carrier_name.title()}TrackingEventType]
|
784
|
+
"""
|
785
|
+
|
786
|
+
classes = [
|
787
|
+
f"{carrier_name.title()}TrackingRequestType",
|
788
|
+
f"{carrier_name.title()}TrackingEventType",
|
789
|
+
f"{carrier_name.title()}TrackingResponseType"
|
790
|
+
]
|
791
|
+
|
792
|
+
return {"code": code, "classes": classes}
|
793
|
+
|
794
|
+
|
795
|
+
def _generate_basic_schema(carrier_name: str, schema_type: str) -> typing.Dict[str, typing.Any]:
|
796
|
+
"""Generate basic schema for any type."""
|
797
|
+
code = f"""# Generated schema for {carrier_name} - {schema_type}
|
798
|
+
import attr
|
799
|
+
import jstruct
|
800
|
+
import typing
|
801
|
+
|
802
|
+
@attr.s(auto_attribs=True)
|
803
|
+
class {carrier_name.title()}{schema_type.title()}ResponseType:
|
804
|
+
\"\"\"Generated schema for {carrier_name} {schema_type} response.\"\"\"
|
805
|
+
status: typing.Optional[str] = None
|
806
|
+
data: typing.Optional[typing.Dict[str, typing.Any]] = None
|
807
|
+
# Add more fields based on API documentation
|
808
|
+
"""
|
809
|
+
|
810
|
+
classes = [f"{carrier_name.title()}{schema_type.title()}ResponseType"]
|
811
|
+
|
812
|
+
return {"code": code, "classes": classes}
|
813
|
+
|
814
|
+
|
815
|
+
# Import enhanced tools
|
816
|
+
from .enhanced_tools import (
|
817
|
+
create_karrio_plugin_structure,
|
818
|
+
get_karrio_plugin_structure_info,
|
819
|
+
analyze_carrier_api_documentation,
|
820
|
+
scrape_api_documentation,
|
821
|
+
extract_openapi_from_url
|
822
|
+
)
|
823
|
+
|
824
|
+
# Create FunctionTool instances
|
825
|
+
analyze_existing_connector_tool = FunctionTool(analyze_existing_connector)
|
826
|
+
extract_carrier_patterns_tool = FunctionTool(extract_carrier_patterns)
|
827
|
+
generate_carrier_schema_tool = FunctionTool(generate_carrier_schema)
|
828
|
+
generate_carrier_mappings_tool = FunctionTool(generate_carrier_mappings)
|
829
|
+
generate_integration_tests_tool = FunctionTool(generate_integration_tests)
|
830
|
+
assemble_complete_integration_tool = FunctionTool(assemble_complete_integration)
|
831
|
+
|
832
|
+
# Enhanced tools for proper plugin generation
|
833
|
+
create_plugin_structure_tool = FunctionTool(create_karrio_plugin_structure)
|
834
|
+
get_plugin_structure_info_tool = FunctionTool(get_karrio_plugin_structure_info)
|
835
|
+
analyze_api_documentation_tool = FunctionTool(analyze_carrier_api_documentation)
|
836
|
+
scrape_api_documentation_tool = FunctionTool(scrape_api_documentation)
|
837
|
+
extract_openapi_from_url_tool = FunctionTool(extract_openapi_from_url)
|
838
|
+
|
839
|
+
# Specialized Sub-Agents
|
840
|
+
schema_agent = Agent(
|
841
|
+
name="schema_agent",
|
842
|
+
model="gemini-1.5-flash",
|
843
|
+
description="Specialized agent for generating Python schemas from carrier API documentation",
|
844
|
+
instruction="""
|
845
|
+
You are a specialized agent for generating Python dataclasses and schemas from carrier API documentation.
|
846
|
+
|
847
|
+
Your expertise includes:
|
848
|
+
- Converting JSON schemas to Python dataclasses with proper typing
|
849
|
+
- Using attrs and jstruct decorators for serialization
|
850
|
+
- Following Karrio's schema conventions
|
851
|
+
- Handling complex nested types and optional fields
|
852
|
+
- Generating appropriate imports and type hints
|
853
|
+
|
854
|
+
When generating schemas:
|
855
|
+
1. Always use attrs with auto_attribs=True
|
856
|
+
2. Use jstruct decorators for JSON serialization
|
857
|
+
3. Append 'Type' suffix to class names unless they already end with 'Type'
|
858
|
+
4. Use typing module for all type annotations
|
859
|
+
5. Handle Optional, List, Union, and Dict types properly
|
860
|
+
6. Generate proper default values using jstruct helpers
|
861
|
+
|
862
|
+
You work with the main integration agent to ensure schema compatibility.
|
863
|
+
""",
|
864
|
+
tools=[generate_carrier_schema_tool],
|
865
|
+
)
|
866
|
+
|
867
|
+
mapping_agent = Agent(
|
868
|
+
name="mapping_agent",
|
869
|
+
model="gemini-1.5-flash",
|
870
|
+
description="Specialized agent for generating API request/response mappings",
|
871
|
+
instruction="""
|
872
|
+
You are a specialized agent for generating carrier API mappings and transformations.
|
873
|
+
|
874
|
+
Your expertise includes:
|
875
|
+
- Creating request/response mapping functions
|
876
|
+
- Handling API authentication and headers
|
877
|
+
- Converting between Karrio models and carrier-specific formats
|
878
|
+
- Error handling and validation
|
879
|
+
- Rate, shipment, and tracking API mappings
|
880
|
+
|
881
|
+
When generating mappings:
|
882
|
+
1. Follow Karrio's mapping patterns from existing connectors
|
883
|
+
2. Use proper error handling with Karrio's error models
|
884
|
+
3. Implement proper request/response transformations
|
885
|
+
4. Handle carrier-specific authentication methods
|
886
|
+
5. Support all required operations (rates, shipments, tracking)
|
887
|
+
6. Include proper logging and debugging information
|
888
|
+
|
889
|
+
You integrate with the schema agent for data models and the integration agent for final assembly.
|
890
|
+
""",
|
891
|
+
tools=[generate_carrier_mappings_tool, extract_carrier_patterns_tool],
|
892
|
+
)
|
893
|
+
|
894
|
+
integration_agent = Agent(
|
895
|
+
name="integration_agent",
|
896
|
+
model="gemini-1.5-flash",
|
897
|
+
description="Main integration agent that orchestrates complete carrier integration using official Karrio tools",
|
898
|
+
instruction="""
|
899
|
+
You are the main integration agent responsible for orchestrating complete carrier integrations using official Karrio CLI tools.
|
900
|
+
|
901
|
+
IMPORTANT: You must use the official Karrio plugin generation workflow:
|
902
|
+
1. Use `kcli sdk add-extension` command to generate proper plugin structure
|
903
|
+
2. Guide users through the official workflow, not manual file creation
|
904
|
+
3. Provide accurate structure information based on real Karrio templates
|
905
|
+
|
906
|
+
Your capabilities include:
|
907
|
+
- Creating plugin structures using official CLI tools (create_plugin_structure_tool)
|
908
|
+
- Analyzing carrier API documentation to determine plugin configuration (analyze_api_documentation_tool)
|
909
|
+
- Providing accurate plugin structure information (get_plugin_structure_info_tool)
|
910
|
+
- Coordinating with specialized sub-agents for implementation details
|
911
|
+
- Ensuring proper Karrio project structure conventions
|
912
|
+
|
913
|
+
When a user asks to build a carrier integration:
|
914
|
+
1. If they provide a URL, use scrape_api_documentation_tool or extract_openapi_from_url_tool to fetch the API docs
|
915
|
+
2. If they provide API documentation directly, analyze it using analyze_api_documentation_tool
|
916
|
+
3. Use the official plugin generation tools to create proper structure
|
917
|
+
4. Provide step-by-step instructions using kcli commands
|
918
|
+
5. Guide them through the implementation workflow
|
919
|
+
6. Ensure they understand the proper file structure and implementation pattern
|
920
|
+
|
921
|
+
You ensure 95%+ completion by:
|
922
|
+
- Using official Karrio tooling and templates
|
923
|
+
- Following the proper provider/mapper/schema separation
|
924
|
+
- Including all necessary components for production use
|
925
|
+
- Providing accurate implementation guidance based on real Karrio patterns
|
926
|
+
""",
|
927
|
+
tools=[
|
928
|
+
analyze_existing_connector_tool,
|
929
|
+
extract_carrier_patterns_tool,
|
930
|
+
assemble_complete_integration_tool,
|
931
|
+
create_plugin_structure_tool,
|
932
|
+
get_plugin_structure_info_tool,
|
933
|
+
analyze_api_documentation_tool,
|
934
|
+
scrape_api_documentation_tool,
|
935
|
+
extract_openapi_from_url_tool
|
936
|
+
],
|
937
|
+
)
|
938
|
+
|
939
|
+
testing_agent = Agent(
|
940
|
+
name="testing_agent",
|
941
|
+
model="gemini-1.5-flash",
|
942
|
+
description="Specialized agent for generating comprehensive test suites",
|
943
|
+
instruction="""
|
944
|
+
You are a specialized agent for generating comprehensive test suites for carrier integrations.
|
945
|
+
|
946
|
+
Your expertise includes:
|
947
|
+
- Creating unit tests for individual components
|
948
|
+
- Generating integration tests for end-to-end workflows
|
949
|
+
- Building test fixtures and mock data
|
950
|
+
- Testing error handling and edge cases
|
951
|
+
- Performance and reliability testing
|
952
|
+
|
953
|
+
When generating tests:
|
954
|
+
1. Create tests for all major functionality (rates, shipments, tracking)
|
955
|
+
2. Include both positive and negative test cases
|
956
|
+
3. Generate realistic test data and fixtures
|
957
|
+
4. Test error handling and validation
|
958
|
+
5. Follow pytest conventions and best practices
|
959
|
+
6. Include integration tests with mock API responses
|
960
|
+
7. Generate performance benchmarks where appropriate
|
961
|
+
|
962
|
+
You work with the integration agent to ensure complete test coverage.
|
963
|
+
""",
|
964
|
+
tools=[generate_integration_tests_tool],
|
965
|
+
)
|
966
|
+
|
967
|
+
# Main Karrio Integration Agent - Orchestrates sub-agents
|
968
|
+
root_agent = SequentialAgent(
|
969
|
+
name="karrio_integration_agent",
|
970
|
+
description="Advanced Karrio shipping carrier integration agent with multi-agent architecture and RAG capabilities",
|
971
|
+
sub_agents=[schema_agent, mapping_agent, integration_agent, testing_agent],
|
972
|
+
)
|