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.
Files changed (68) hide show
  1. karrio_cli/__init__.py +0 -0
  2. karrio_cli/__main__.py +105 -0
  3. karrio_cli/ai/README.md +335 -0
  4. karrio_cli/ai/__init__.py +0 -0
  5. karrio_cli/ai/commands.py +102 -0
  6. karrio_cli/ai/karrio_ai/__init__.py +1 -0
  7. karrio_cli/ai/karrio_ai/agent.py +972 -0
  8. karrio_cli/ai/karrio_ai/architecture/INTEGRATION_AGENT_PROMPT.md +497 -0
  9. karrio_cli/ai/karrio_ai/architecture/MAPPING_AGENT_PROMPT.md +355 -0
  10. karrio_cli/ai/karrio_ai/architecture/REAL_WORLD_TESTING.md +305 -0
  11. karrio_cli/ai/karrio_ai/architecture/SCHEMA_AGENT_PROMPT.md +183 -0
  12. karrio_cli/ai/karrio_ai/architecture/TESTING_AGENT_PROMPT.md +448 -0
  13. karrio_cli/ai/karrio_ai/architecture/TESTING_GUIDE.md +271 -0
  14. karrio_cli/ai/karrio_ai/enhanced_tools.py +943 -0
  15. karrio_cli/ai/karrio_ai/rag_system.py +503 -0
  16. karrio_cli/ai/karrio_ai/tests/test_agent.py +350 -0
  17. karrio_cli/ai/karrio_ai/tests/test_real_integration.py +360 -0
  18. karrio_cli/ai/karrio_ai/tests/test_real_world_scenarios.py +513 -0
  19. karrio_cli/commands/__init__.py +0 -0
  20. karrio_cli/commands/codegen.py +336 -0
  21. karrio_cli/commands/login.py +139 -0
  22. karrio_cli/commands/plugins.py +168 -0
  23. karrio_cli/commands/sdk.py +870 -0
  24. karrio_cli/common/queries.py +101 -0
  25. karrio_cli/common/utils.py +368 -0
  26. karrio_cli/resources/__init__.py +0 -0
  27. karrio_cli/resources/carriers.py +91 -0
  28. karrio_cli/resources/connections.py +207 -0
  29. karrio_cli/resources/events.py +151 -0
  30. karrio_cli/resources/logs.py +151 -0
  31. karrio_cli/resources/orders.py +144 -0
  32. karrio_cli/resources/shipments.py +210 -0
  33. karrio_cli/resources/trackers.py +287 -0
  34. karrio_cli/templates/__init__.py +9 -0
  35. karrio_cli/templates/__pycache__/__init__.cpython-311.pyc +0 -0
  36. karrio_cli/templates/__pycache__/__init__.cpython-312.pyc +0 -0
  37. karrio_cli/templates/__pycache__/address.cpython-311.pyc +0 -0
  38. karrio_cli/templates/__pycache__/address.cpython-312.pyc +0 -0
  39. karrio_cli/templates/__pycache__/docs.cpython-311.pyc +0 -0
  40. karrio_cli/templates/__pycache__/docs.cpython-312.pyc +0 -0
  41. karrio_cli/templates/__pycache__/documents.cpython-311.pyc +0 -0
  42. karrio_cli/templates/__pycache__/documents.cpython-312.pyc +0 -0
  43. karrio_cli/templates/__pycache__/manifest.cpython-311.pyc +0 -0
  44. karrio_cli/templates/__pycache__/manifest.cpython-312.pyc +0 -0
  45. karrio_cli/templates/__pycache__/pickup.cpython-311.pyc +0 -0
  46. karrio_cli/templates/__pycache__/pickup.cpython-312.pyc +0 -0
  47. karrio_cli/templates/__pycache__/rates.cpython-311.pyc +0 -0
  48. karrio_cli/templates/__pycache__/rates.cpython-312.pyc +0 -0
  49. karrio_cli/templates/__pycache__/sdk.cpython-311.pyc +0 -0
  50. karrio_cli/templates/__pycache__/sdk.cpython-312.pyc +0 -0
  51. karrio_cli/templates/__pycache__/shipments.cpython-311.pyc +0 -0
  52. karrio_cli/templates/__pycache__/shipments.cpython-312.pyc +0 -0
  53. karrio_cli/templates/__pycache__/tracking.cpython-311.pyc +0 -0
  54. karrio_cli/templates/__pycache__/tracking.cpython-312.pyc +0 -0
  55. karrio_cli/templates/address.py +308 -0
  56. karrio_cli/templates/docs.py +150 -0
  57. karrio_cli/templates/documents.py +428 -0
  58. karrio_cli/templates/manifest.py +396 -0
  59. karrio_cli/templates/pickup.py +839 -0
  60. karrio_cli/templates/rates.py +638 -0
  61. karrio_cli/templates/sdk.py +947 -0
  62. karrio_cli/templates/shipments.py +892 -0
  63. karrio_cli/templates/tracking.py +437 -0
  64. karrio_cli-2025.5rc3.dist-info/METADATA +165 -0
  65. karrio_cli-2025.5rc3.dist-info/RECORD +68 -0
  66. karrio_cli-2025.5rc3.dist-info/WHEEL +5 -0
  67. karrio_cli-2025.5rc3.dist-info/entry_points.txt +2 -0
  68. 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
+ )