jit-protocol 1.0.0__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.
jit_api/__init__.py ADDED
@@ -0,0 +1,39 @@
1
+ """
2
+ JIT Protocol Synthesis Framework - Python Host Engine (jit-api-python)
3
+ Dynamic Semantic Negotiation ➔ Static Code Freeze ➔ Fallback
4
+ Powered by TypeSafe (Jev) and Cactus Needle 3.0
5
+ """
6
+
7
+ from .engine import JITEngine
8
+ from .types import (
9
+ IRField,
10
+ IRSchema,
11
+ JITExecutionResult,
12
+ JITRequestContext,
13
+ LifecyclePhase,
14
+ RouteDefinition,
15
+ StabilityMetrics,
16
+ )
17
+ from .typesafe_client import TypeSafeClient
18
+ from .needle_engine import NeedleEngine
19
+ from .router import TypeSafeRouter
20
+ from .observer import SchemaObserver
21
+ from .fallback_handler import FallbackHandler
22
+ from .codegen import CodegenEngine
23
+
24
+ __all__ = [
25
+ "JITEngine",
26
+ "TypeSafeClient",
27
+ "NeedleEngine",
28
+ "TypeSafeRouter",
29
+ "SchemaObserver",
30
+ "FallbackHandler",
31
+ "CodegenEngine",
32
+ "IRField",
33
+ "IRSchema",
34
+ "JITExecutionResult",
35
+ "JITRequestContext",
36
+ "LifecyclePhase",
37
+ "RouteDefinition",
38
+ "StabilityMetrics",
39
+ ]
jit_api/codegen.py ADDED
@@ -0,0 +1,188 @@
1
+ import json
2
+ import os
3
+ from typing import Any, Dict, Optional
4
+ from .types import IRField, IRSchema
5
+
6
+
7
+ class CodegenEngine:
8
+ """
9
+ Compiles IRSchema into multi-language contracts (TypeScript, Go, Python, IR JSON).
10
+ """
11
+
12
+ def __init__(self, output_dir: Optional[str] = None):
13
+ self.output_dir = output_dir or os.path.abspath("generated")
14
+
15
+ def _ensure_dirs(self) -> None:
16
+ for sub in ["typescript", "golang", "python", "schemas"]:
17
+ os.makedirs(os.path.join(self.output_dir, sub), exist_ok=True)
18
+
19
+ def generate_python(self, schema: IRSchema) -> str:
20
+ fields_code = []
21
+ for f in schema.fields.values():
22
+ py_t = "str"
23
+ if f.type == "number":
24
+ py_t = "float"
25
+ elif f.type == "boolean":
26
+ py_t = "bool"
27
+ elif f.type == "array":
28
+ item_t = "float" if f.item_type == "number" else ("bool" if f.item_type == "boolean" else "str")
29
+ py_t = f"list[{item_t}]"
30
+ elif f.type == "object":
31
+ py_t = "dict[str, Any]"
32
+
33
+ if not f.required:
34
+ py_t = f"Optional[{py_t}] = None"
35
+
36
+ fields_code.append(f" {f.name}: {py_t}")
37
+
38
+ return f'''"""
39
+ AUTO-GENERATED BY JIT PROTOCOL SYNTHESIS FRAMEWORK (Python)
40
+ Route: {schema.route} (v{schema.version})
41
+ Frozen At: {schema.frozen_at}
42
+ """
43
+
44
+ from typing import Any, Optional
45
+ from pydantic import BaseModel
46
+ from fastapi import APIRouter
47
+
48
+ router = APIRouter(prefix="/{schema.route}", tags=["{schema.route}"])
49
+
50
+
51
+ class {schema.name}(BaseModel):
52
+ {chr(10).join(fields_code)}
53
+
54
+
55
+ @router.post("/", response_model=dict[str, Any])
56
+ async def handle_{schema.route.replace("-", "_")}(payload: {schema.name}):
57
+ return {{
58
+ "status": "success",
59
+ "route": "{schema.route}",
60
+ "version": {schema.version},
61
+ "data": payload.model_dump(),
62
+ }}
63
+ '''
64
+
65
+ def generate_typescript(self, schema: IRSchema) -> str:
66
+ fields_code = []
67
+ for f in schema.fields.values():
68
+ z_t = "z.string()"
69
+ if f.type == "number":
70
+ z_t = "z.number()"
71
+ elif f.type == "boolean":
72
+ z_t = "z.boolean()"
73
+ elif f.type == "array":
74
+ item_z = "z.number()" if f.item_type == "number" else ("z.boolean()" if f.item_type == "boolean" else "z.string()")
75
+ z_t = f"z.array({item_z})"
76
+ elif f.type == "object":
77
+ z_t = "z.record(z.unknown())"
78
+
79
+ if not f.required:
80
+ z_t += ".optional()"
81
+ fields_code.append(f" {f.name}: {z_t},")
82
+
83
+ return f'''/**
84
+ * AUTO-GENERATED BY JIT PROTOCOL SYNTHESIS FRAMEWORK (Python Host)
85
+ * Route: {schema.route} (v{schema.version})
86
+ */
87
+
88
+ import {{ z }} from 'zod';
89
+
90
+ export const {schema.name}Schema = z.object({{
91
+ {chr(10).join(fields_code)}
92
+ }});
93
+
94
+ export type {schema.name} = z.infer<typeof {schema.name}Schema>;
95
+ '''
96
+
97
+ def generate_golang(self, schema: IRSchema) -> Dict[str, str]:
98
+ # 1. Proto3
99
+ proto_fields = []
100
+ for idx, f in enumerate(schema.fields.values(), 1):
101
+ pr_t = "string"
102
+ if f.type == "number":
103
+ pr_t = "double"
104
+ elif f.type == "boolean":
105
+ pr_t = "bool"
106
+ elif f.type == "array":
107
+ pr_t = f"repeated {'double' if f.item_type == 'number' else 'string'}"
108
+ proto_fields.append(f" {pr_t} {f.name} = {idx};")
109
+
110
+ proto = f'''// AUTO-GENERATED BY JIT PROTOCOL SYNTHESIS FRAMEWORK
111
+ syntax = "proto3";
112
+
113
+ package jit.v{schema.version};
114
+
115
+ option go_package = "jit/gen/v{schema.version};jitv{schema.version}";
116
+
117
+ message {schema.name} {{
118
+ {chr(10).join(proto_fields)}
119
+ }}
120
+ '''
121
+ # 2. Go Struct
122
+ go_fields = []
123
+ for f in schema.fields.values():
124
+ field_name = "".join(part.capitalize() for part in f.name.split("_"))
125
+ go_t = "string"
126
+ if f.type == "number":
127
+ go_t = "float64"
128
+ elif f.type == "boolean":
129
+ go_t = "bool"
130
+ elif f.type == "array":
131
+ go_t = "[]float64" if f.item_type == "number" else "[]string"
132
+ elif f.type == "object":
133
+ go_t = "map[string]interface{}"
134
+ go_fields.append(f'\t{field_name} {go_t} `json:"{f.name}"`')
135
+
136
+ go_struct = f'''// AUTO-GENERATED BY JIT PROTOCOL SYNTHESIS FRAMEWORK
137
+ package models
138
+
139
+ type {schema.name} struct {{
140
+ {chr(10).join(go_fields)}
141
+ }}
142
+ '''
143
+ return {"proto": proto, "golang": go_struct}
144
+
145
+ def compile(self, schema: IRSchema) -> Dict[str, Any]:
146
+ self._ensure_dirs()
147
+ base = schema.route.replace("-", "_")
148
+
149
+ py_code = self.generate_python(schema)
150
+ py_path = os.path.join(self.output_dir, "python", f"{base}.py")
151
+ with open(py_path, "w", encoding="utf-8") as f:
152
+ f.write(py_code)
153
+
154
+ ts_code = self.generate_typescript(schema)
155
+ ts_path = os.path.join(self.output_dir, "typescript", f"{base}.ts")
156
+ with open(ts_path, "w", encoding="utf-8") as f:
157
+ f.write(ts_code)
158
+
159
+ go_res = self.generate_golang(schema)
160
+ proto_path = os.path.join(self.output_dir, "golang", f"{base}.proto")
161
+ with open(proto_path, "w", encoding="utf-8") as f:
162
+ f.write(go_res["proto"])
163
+
164
+ go_path = os.path.join(self.output_dir, "golang", f"{base}.go")
165
+ with open(go_path, "w", encoding="utf-8") as f:
166
+ f.write(go_res["golang"])
167
+
168
+ ir_dict = {
169
+ "name": schema.name,
170
+ "version": schema.version,
171
+ "route": schema.route,
172
+ "fields": {k: v.__dict__ for k, v in schema.fields.items()},
173
+ "frozen_at": schema.frozen_at,
174
+ }
175
+ ir_path = os.path.join(self.output_dir, "schemas", f"{base}.json")
176
+ with open(ir_path, "w", encoding="utf-8") as f:
177
+ json.dump(ir_dict, f, indent=2)
178
+
179
+ return {
180
+ "schema": schema,
181
+ "files": {
182
+ "python": py_path,
183
+ "typescript": ts_path,
184
+ "proto": proto_path,
185
+ "golang": go_path,
186
+ "ir_json": ir_path,
187
+ },
188
+ }
jit_api/engine.py ADDED
@@ -0,0 +1,200 @@
1
+ import inspect
2
+ import time
3
+ from typing import Any, Callable, Dict, List, Optional, Type, Union
4
+ from pydantic import BaseModel, ValidationError, create_model
5
+
6
+ from .codegen import CodegenEngine
7
+ from .fallback_handler import FallbackHandler
8
+ from .needle_engine import NeedleEngine
9
+ from .observer import SchemaObserver
10
+ from .router import TypeSafeRouter
11
+ from .types import (
12
+ IRSchema,
13
+ JITExecutionResult,
14
+ JITRequestContext,
15
+ RouteDefinition,
16
+ RouteHandler,
17
+ )
18
+ from .typesafe_client import TypeSafeClient
19
+
20
+
21
+ class JITEngine:
22
+ def __init__(
23
+ self,
24
+ client: Optional[TypeSafeClient] = None,
25
+ needle_engine: Optional[NeedleEngine] = None,
26
+ stability_threshold: int = 5,
27
+ confidence_threshold: float = 0.85,
28
+ fallback_to_needle: bool = True,
29
+ force_needle: bool = False,
30
+ codegen_output_dir: Optional[str] = None,
31
+ on_freeze: Optional[Callable[[Dict[str, Any]], None]] = None,
32
+ on_drift: Optional[Callable[[str, str], None]] = None,
33
+ ):
34
+ self.router = TypeSafeRouter(
35
+ client=client,
36
+ needle_engine=needle_engine,
37
+ fallback_to_needle=fallback_to_needle,
38
+ force_needle=force_needle,
39
+ )
40
+ self.codegen = CodegenEngine(codegen_output_dir)
41
+ self.fast_path_validators: Dict[str, Type[BaseModel]] = {}
42
+ self.on_freeze_callback = on_freeze
43
+ self.on_drift_callback = on_drift
44
+
45
+ async def _internal_on_freeze(schema: IRSchema):
46
+ # 1. Compile static files across TS, Go, Python, IR
47
+ codegen_res = self.codegen.compile(schema)
48
+
49
+ # 2. Build dynamic in-memory Pydantic v2 validator for Phase 3
50
+ self._mount_fast_path_validator(schema)
51
+
52
+ if self.on_freeze_callback:
53
+ if inspect.iscoroutinefunction(self.on_freeze_callback):
54
+ await self.on_freeze_callback(codegen_res)
55
+ else:
56
+ self.on_freeze_callback(codegen_res)
57
+
58
+ def _internal_on_drift(route: str, error: str, payload: Dict[str, Any]):
59
+ # Remove fast-path validator to re-enter dynamic Phase 1 mode
60
+ self.fast_path_validators.pop(route, None)
61
+ if self.on_drift_callback:
62
+ self.on_drift_callback(route, error)
63
+
64
+ self.observer = SchemaObserver(
65
+ stability_threshold=stability_threshold,
66
+ confidence_threshold=confidence_threshold,
67
+ on_freeze=_internal_on_freeze,
68
+ )
69
+
70
+ self.fallback = FallbackHandler(
71
+ router=self.router,
72
+ observer=self.observer,
73
+ on_drift_detected=_internal_on_drift,
74
+ )
75
+
76
+ def register(self, route_def: RouteDefinition) -> "JITEngine":
77
+ self.router.register(route_def)
78
+ return self
79
+
80
+ def route(
81
+ self,
82
+ name: str,
83
+ description: str,
84
+ intent: str,
85
+ enum_fields: Optional[Dict[str, Dict[str, str]]] = None,
86
+ ) -> Callable[[RouteHandler], RouteHandler]:
87
+ """
88
+ Pythonic decorator to register a route handler:
89
+ @engine.route(name="create_invoice", description="...", intent="...")
90
+ async def create_invoice(payload, ctx):
91
+ return {"status": "ok"}
92
+ """
93
+ return self.router.route(
94
+ name=name,
95
+ description=description,
96
+ intent=intent,
97
+ enum_fields=enum_fields,
98
+ )
99
+
100
+ def _mount_fast_path_validator(self, schema: IRSchema) -> None:
101
+ """
102
+ Dynamically create high-speed Pydantic v2 model for 0ms Phase 3 execution
103
+ """
104
+ field_definitions: Dict[str, Any] = {}
105
+
106
+ for name, field_meta in schema.fields.items():
107
+ py_type = str
108
+ if field_meta.type == "number":
109
+ py_type = float
110
+ elif field_meta.type == "boolean":
111
+ py_type = bool
112
+ elif field_meta.type == "array":
113
+ item_t = (
114
+ float
115
+ if field_meta.item_type == "number"
116
+ else (bool if field_meta.item_type == "boolean" else str)
117
+ )
118
+ py_type = List[item_t]
119
+ elif field_meta.type == "object":
120
+ py_type = Dict[str, Any]
121
+
122
+ default_val = ... if field_meta.required else None
123
+ field_definitions[name] = (py_type, default_val)
124
+
125
+ model = create_model(f"{schema.name}FastValidator", **field_definitions)
126
+ self.fast_path_validators[schema.route] = model
127
+
128
+ async def execute(
129
+ self, payload: Dict[str, Any], explicit_route: Optional[str] = None
130
+ ) -> JITExecutionResult:
131
+ start_time = time.time()
132
+ target_route = explicit_route or (
133
+ payload.get("route") if isinstance(payload.get("route"), str) else None
134
+ )
135
+
136
+ is_target_frozen = target_route and self.observer.is_frozen(target_route)
137
+
138
+ # -------------------------------------------------------------
139
+ # Phase 3: Static Fast-Path (Pydantic v2, 0ms AI Latency)
140
+ # -------------------------------------------------------------
141
+ if is_target_frozen and target_route:
142
+ validator = self.fast_path_validators.get(target_route)
143
+ route_def = self.router.get_route(target_route)
144
+
145
+ if validator and route_def:
146
+ try:
147
+ # Validate natively in-memory via Pydantic v2 C-extension
148
+ validated = validator.model_validate(payload)
149
+ clean_data = validated.model_dump()
150
+
151
+ exec_time_ms = round((time.time() - start_time) * 1000, 2)
152
+ ctx = JITRequestContext(
153
+ route=target_route,
154
+ phase="phase3_frozen",
155
+ execution_time_ms=exec_time_ms,
156
+ ai_latency_ms=0.0, # 0ms AI latency!
157
+ intent_confidence=1.0,
158
+ is_fallback=False,
159
+ )
160
+
161
+ data = await self.router._invoke_handler(
162
+ route_def.handler, clean_data, ctx
163
+ )
164
+ return JITExecutionResult(success=True, data=data, context=ctx)
165
+ except ValidationError as val_err:
166
+ # Schema Drift detected! Downgrade to Phase 1 & re-arm Phase 2 for v2
167
+ fallback_res = await self.fallback.handle_fallback(
168
+ target_route, str(val_err), payload
169
+ )
170
+ return JITExecutionResult(
171
+ success=True,
172
+ data=fallback_res["result"],
173
+ context=fallback_res["context"],
174
+ )
175
+
176
+ # -------------------------------------------------------------
177
+ # Phase 1: Dynamic Semantic Routing (TypeSafe Jev or Needle)
178
+ # -------------------------------------------------------------
179
+ res = await self.router.handle(payload, preferred_route=target_route)
180
+
181
+ # Phase 2: Observation & Stability Tracking
182
+ obs = await self.observer.observe(
183
+ res["route"],
184
+ res["normalized_payload"],
185
+ res["context"].intent_confidence,
186
+ )
187
+
188
+ res["context"].phase = "phase3_frozen" if obs["stable"] else "phase1_dynamic"
189
+
190
+ return JITExecutionResult(
191
+ success=True, data=res["result"], context=res["context"]
192
+ )
193
+
194
+ def get_route_status(self, route: str) -> Dict[str, Any]:
195
+ return {
196
+ "route": route,
197
+ "is_frozen": self.observer.is_frozen(route),
198
+ "metrics": self.observer.get_metrics(route),
199
+ "frozen_schema": self.observer.get_frozen_schema(route),
200
+ }
@@ -0,0 +1,51 @@
1
+ import sys
2
+ from typing import Any, Callable, Dict, Optional
3
+ from .observer import SchemaObserver
4
+ from .router import TypeSafeRouter
5
+
6
+
7
+ class FallbackHandler:
8
+ """
9
+ Handles Phase 3 Fast-Path validation errors (Schema Drift)
10
+ by downgrading the request back to Phase 1 dynamic semantic resolution
11
+ and re-arming Phase 2 observation for the next contract version (v2, v3...).
12
+ """
13
+
14
+ def __init__(
15
+ self,
16
+ router: TypeSafeRouter,
17
+ observer: SchemaObserver,
18
+ on_drift_detected: Optional[Callable[[str, str, Dict[str, Any]], None]] = None,
19
+ ):
20
+ self.router = router
21
+ self.observer = observer
22
+ self.on_drift_detected = on_drift_detected or self._default_log_drift
23
+
24
+ @staticmethod
25
+ def _default_log_drift(route: str, error: str, payload: Dict[str, Any]) -> None:
26
+ sys.stderr.write(
27
+ f"[FallbackHandler] ⚠️ Schema drift detected on '{route}': {error}. Downgrading to Phase 1.\n"
28
+ )
29
+ sys.stderr.flush()
30
+
31
+ async def handle_fallback(
32
+ self, route: str, validation_error: str, raw_payload: Dict[str, Any]
33
+ ) -> Dict[str, Any]:
34
+ # 1. Notify drift
35
+ self.on_drift_detected(route, validation_error, raw_payload)
36
+
37
+ # 2. Reset observer for this route to initiate v2 observation
38
+ self.observer.reset_route(route)
39
+
40
+ # 3. Reroute through Phase 1 dynamic semantic router
41
+ res = await self.router.handle(raw_payload, preferred_route=route)
42
+
43
+ # 4. Mark context as fallback
44
+ res["context"].is_fallback = True
45
+
46
+ # 5. Record drifted payload as sample 1 of the new version
47
+ await self.observer.observe(
48
+ route, res["normalized_payload"], res["context"].intent_confidence
49
+ )
50
+
51
+ return res
@@ -0,0 +1,159 @@
1
+ import json
2
+ import re
3
+ import time
4
+ from typing import Any, Dict, List, Optional
5
+ from .types import RouteDefinition
6
+
7
+ NEEDLE_NATIVE_AVAILABLE = False
8
+ try:
9
+ import needle
10
+ NEEDLE_NATIVE_AVAILABLE = True
11
+ except ImportError:
12
+ NEEDLE_NATIVE_AVAILABLE = False
13
+
14
+
15
+ class NeedleEngine:
16
+ """
17
+ In-process Native Cactus Needle 3.0 Engine.
18
+ Executes tool calling and structured extraction locally with 0 API cost.
19
+ """
20
+
21
+ def __init__(self, enable_guardrail: bool = True):
22
+ self.enable_guardrail = enable_guardrail
23
+ self.is_native_available = NEEDLE_NATIVE_AVAILABLE
24
+
25
+ def route_to_needle_tool(self, route_def: RouteDefinition) -> Dict[str, Any]:
26
+ properties: Dict[str, Any] = {}
27
+
28
+ if route_def.enum_fields:
29
+ for field_name, criteria in route_def.enum_fields.items():
30
+ properties[field_name] = {
31
+ "type": "string",
32
+ "enum": list(criteria.keys()),
33
+ "description": f"Allowed options: {', '.join(criteria.keys())}",
34
+ }
35
+
36
+ return {
37
+ "name": route_def.route,
38
+ "description": f"{route_def.description}. Intent criteria: {route_def.intent_criteria}",
39
+ "parameters": {
40
+ "type": "object",
41
+ "properties": properties,
42
+ },
43
+ }
44
+
45
+ def check_security_guardrail(self, payload: Dict[str, Any]) -> None:
46
+ if not self.enable_guardrail:
47
+ return
48
+
49
+ dangerous_patterns = [
50
+ r"('|\b;|--|\/\*|\b(DROP\s+TABLE|UNION\s+SELECT|ALTER\s+TABLE|EXEC\s*\()\b)",
51
+ r"<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>",
52
+ r"\b(rm\s+-rf|chmod\s+777|wget\s+http|curl\s+http)\b",
53
+ ]
54
+
55
+ text = json.dumps(payload)
56
+ for pattern in dangerous_patterns:
57
+ if re.search(pattern, text, re.IGNORECASE):
58
+ raise PermissionError(
59
+ "[NeedleEngine] Request blocked by local security guardrail (Matched exploit pattern)"
60
+ )
61
+
62
+ def execute(
63
+ self,
64
+ raw_input: Dict[str, Any],
65
+ routes: List[RouteDefinition],
66
+ preferred_route: Optional[str] = None,
67
+ ) -> Dict[str, Any]:
68
+ start_time = time.time()
69
+
70
+ # 1. Local Guardrail Check
71
+ self.check_security_guardrail(raw_input)
72
+
73
+ # Bypass if preferredRoute is explicitly targeted
74
+ if preferred_route and any(r.route == preferred_route for r in routes):
75
+ latency_ms = round((time.time() - start_time) * 1000, 2)
76
+ return {
77
+ "route": preferred_route,
78
+ "normalized_payload": dict(raw_input),
79
+ "confidence": 1.0,
80
+ "latency_ms": latency_ms,
81
+ }
82
+
83
+ tools = [self.route_to_needle_tool(r) for r in routes]
84
+ query = (
85
+ raw_input.get("message")
86
+ or raw_input.get("text")
87
+ or json.dumps(raw_input)
88
+ )
89
+
90
+ # 2. Try Native Needle In-Process Execution
91
+ if self.is_native_available:
92
+ try:
93
+ agent = needle.Needle(tools=tools)
94
+ res = agent.run(str(query))
95
+ latency_ms = round((time.time() - start_time) * 1000, 2)
96
+
97
+ function_calls = res.get("function_calls", [])
98
+ if function_calls:
99
+ call = function_calls[0]
100
+ normalized = dict(raw_input)
101
+ normalized.update(call.get("arguments", {}))
102
+ return {
103
+ "route": call["name"],
104
+ "normalized_payload": normalized,
105
+ "confidence": float(res.get("confidence", 0.95)),
106
+ "latency_ms": latency_ms,
107
+ "reasoning": res.get("reasoning"),
108
+ }
109
+ except Exception:
110
+ # If weights download is offline, fall back to semantic matcher
111
+ pass
112
+
113
+ # 3. Deterministic In-Memory Matcher Fallback
114
+ return self._embedded_match(str(query), raw_input, routes, start_time)
115
+
116
+ def _embedded_match(
117
+ self,
118
+ query: str,
119
+ raw_input: Dict[str, Any],
120
+ routes: List[RouteDefinition],
121
+ start_time: float,
122
+ ) -> Dict[str, Any]:
123
+ q_lower = query.lower()
124
+ best_route = routes[0]
125
+ highest_score = -1
126
+
127
+ for r in routes:
128
+ score = 0
129
+ text_corpus = f"{r.route} {r.description} {r.intent_criteria}".lower()
130
+ words = [w for w in re.findall(r"\w+", text_corpus) if len(w) > 2]
131
+
132
+ for w in words:
133
+ if w in q_lower:
134
+ score += 1
135
+
136
+ if score > highest_score:
137
+ highest_score = score
138
+ best_route = r
139
+
140
+ normalized_payload = dict(raw_input)
141
+
142
+ if best_route.enum_fields:
143
+ for field, options in best_route.enum_fields.items():
144
+ if field not in normalized_payload:
145
+ for opt_key in options.keys():
146
+ if opt_key.lower() in q_lower:
147
+ normalized_payload[field] = opt_key
148
+ break
149
+
150
+ confidence = 0.94 if highest_score > 0 else 0.86
151
+ latency_ms = round((time.time() - start_time) * 1000, 2)
152
+
153
+ return {
154
+ "route": best_route.route,
155
+ "normalized_payload": normalized_payload,
156
+ "confidence": confidence,
157
+ "latency_ms": latency_ms,
158
+ "reasoning": f"Needle matched route '{best_route.route}' with score {highest_score}",
159
+ }
jit_api/observer.py ADDED
@@ -0,0 +1,183 @@
1
+ import datetime
2
+ import inspect
3
+ from typing import Any, Callable, Dict, List, Optional, Union
4
+ from .types import IRField, IRFieldType, IRSchema, StabilityMetrics
5
+
6
+
7
+ class RouteObservationState:
8
+ def __init__(self):
9
+ self.samples: List[Dict[str, Any]] = []
10
+ self.current_signature: Optional[str] = None
11
+ self.consecutive_matches: int = 0
12
+ self.is_frozen: bool = False
13
+ self.frozen_schema: Optional[IRSchema] = None
14
+
15
+
16
+ class SchemaObserver:
17
+ def __init__(
18
+ self,
19
+ stability_threshold: int = 5,
20
+ confidence_threshold: float = 0.85,
21
+ on_freeze: Optional[Callable[[IRSchema], Any]] = None,
22
+ ):
23
+ self.stability_threshold = stability_threshold
24
+ self.confidence_threshold = confidence_threshold
25
+ self.on_freeze = on_freeze
26
+ self.states: Dict[str, RouteObservationState] = {}
27
+ self.route_versions: Dict[str, int] = {}
28
+
29
+ def infer_type(self, value: Any) -> IRFieldType:
30
+ if isinstance(value, bool):
31
+ return "boolean"
32
+ if isinstance(value, (int, float)):
33
+ return "number"
34
+ if isinstance(value, list):
35
+ return "array"
36
+ if isinstance(value, dict):
37
+ return "object"
38
+ return "string"
39
+
40
+ def generate_signature(self, payload: Dict[str, Any]) -> str:
41
+ parts = []
42
+ for key in sorted(payload.keys()):
43
+ val = payload[key]
44
+ val_type = self.infer_type(val)
45
+ if val_type == "object" and isinstance(val, dict):
46
+ parts.append(f"{key}:{{{self.generate_signature(val)}}}")
47
+ elif val_type == "array" and isinstance(val, list):
48
+ item_type = self.infer_type(val[0]) if val else "unknown"
49
+ parts.append(f"{key}:array[{item_type}]")
50
+ else:
51
+ parts.append(f"{key}:{val_type}")
52
+ return ";".join(parts)
53
+
54
+ def extract_ir_schema(
55
+ self, route: str, sample: Dict[str, Any], version: int = 1
56
+ ) -> IRSchema:
57
+ fields: Dict[str, IRField] = {}
58
+
59
+ for key, val in sample.items():
60
+ field_type = self.infer_type(val)
61
+ field = IRField(name=key, type=field_type, required=True)
62
+
63
+ if field_type == "array" and isinstance(val, list):
64
+ field.item_type = self.infer_type(val[0]) if val else "string"
65
+ elif field_type == "object" and isinstance(val, dict):
66
+ nested: Dict[str, IRField] = {}
67
+ for sub_k, sub_v in val.items():
68
+ nested[sub_k] = IRField(
69
+ name=sub_k, type=self.infer_type(sub_v), required=True
70
+ )
71
+ field.properties = nested
72
+
73
+ fields[key] = field
74
+
75
+ pascal_name = "".join(part.capitalize() for part in route.split("_")) + "Request"
76
+
77
+ return IRSchema(
78
+ name=pascal_name,
79
+ version=version,
80
+ route=route,
81
+ fields=fields,
82
+ sample_payload=sample,
83
+ frozen_at=datetime.datetime.now(datetime.timezone.utc).isoformat(),
84
+ )
85
+
86
+ def get_route_version(self, route: str) -> int:
87
+ return self.route_versions.get(route, 1)
88
+
89
+ def reset_route(self, route: str) -> None:
90
+ next_version = self.get_route_version(route) + 1
91
+ self.route_versions[route] = next_version
92
+ if route in self.states:
93
+ del self.states[route]
94
+
95
+ def is_frozen(self, route: str) -> bool:
96
+ return self.states.get(route, RouteObservationState()).is_frozen
97
+
98
+ def get_frozen_schema(self, route: str) -> Optional[IRSchema]:
99
+ return self.states.get(route, RouteObservationState()).frozen_schema
100
+
101
+ def get_metrics(self, route: str) -> StabilityMetrics:
102
+ state = self.states.get(route)
103
+ if not state or not state.samples:
104
+ return StabilityMetrics(
105
+ sample_count=0,
106
+ consecutive_matches=0,
107
+ required_threshold=self.stability_threshold,
108
+ confidence_threshold=self.confidence_threshold,
109
+ avg_confidence=0.0,
110
+ is_stable=False,
111
+ )
112
+
113
+ recent = state.samples[-self.stability_threshold :]
114
+ avg_conf = sum(s["confidence"] for s in recent) / len(recent)
115
+ is_stable = (
116
+ state.consecutive_matches >= self.stability_threshold
117
+ and avg_conf >= self.confidence_threshold
118
+ )
119
+
120
+ return StabilityMetrics(
121
+ sample_count=len(state.samples),
122
+ consecutive_matches=state.consecutive_matches,
123
+ required_threshold=self.stability_threshold,
124
+ confidence_threshold=self.confidence_threshold,
125
+ avg_confidence=round(avg_conf, 3),
126
+ is_stable=is_stable,
127
+ )
128
+
129
+ async def observe(
130
+ self, route: str, payload: Dict[str, Any], confidence: float = 1.0
131
+ ) -> Dict[str, Any]:
132
+ state = self.states.setdefault(route, RouteObservationState())
133
+
134
+ if state.is_frozen and state.frozen_schema:
135
+ return {
136
+ "stable": True,
137
+ "consecutive_matches": state.consecutive_matches,
138
+ "metrics": self.get_metrics(route),
139
+ "frozen_schema": state.frozen_schema,
140
+ }
141
+
142
+ signature = self.generate_signature(payload)
143
+ state.samples.append(
144
+ {
145
+ "payload": payload,
146
+ "signature": signature,
147
+ "confidence": confidence,
148
+ }
149
+ )
150
+
151
+ if state.current_signature == signature:
152
+ state.consecutive_matches += 1
153
+ else:
154
+ state.current_signature = signature
155
+ state.consecutive_matches = 1
156
+
157
+ metrics = self.get_metrics(route)
158
+
159
+ if metrics.is_stable and not state.is_frozen:
160
+ state.is_frozen = True
161
+ schema = self.extract_ir_schema(
162
+ route, payload, version=self.get_route_version(route)
163
+ )
164
+ state.frozen_schema = schema
165
+
166
+ if self.on_freeze:
167
+ if inspect.iscoroutinefunction(self.on_freeze):
168
+ await self.on_freeze(schema)
169
+ else:
170
+ self.on_freeze(schema)
171
+
172
+ return {
173
+ "stable": True,
174
+ "consecutive_matches": state.consecutive_matches,
175
+ "metrics": metrics,
176
+ "frozen_schema": schema,
177
+ }
178
+
179
+ return {
180
+ "stable": False,
181
+ "consecutive_matches": state.consecutive_matches,
182
+ "metrics": metrics,
183
+ }
jit_api/router.py ADDED
@@ -0,0 +1,194 @@
1
+ import asyncio
2
+ import inspect
3
+ import time
4
+ from typing import Any, Callable, Dict, List, Optional
5
+ from .needle_engine import NeedleEngine
6
+ from .types import JITRequestContext, RouteDefinition, RouteHandler
7
+ from .typesafe_client import TypeSafeClient
8
+
9
+
10
+ class TypeSafeRouter:
11
+ def __init__(
12
+ self,
13
+ client: Optional[TypeSafeClient] = None,
14
+ needle_engine: Optional[NeedleEngine] = None,
15
+ fallback_to_needle: bool = True,
16
+ force_needle: bool = False,
17
+ security_threshold: float = 0.8,
18
+ ):
19
+ self.client = client or TypeSafeClient()
20
+ self.needle_engine = needle_engine or NeedleEngine()
21
+ self.fallback_to_needle = fallback_to_needle
22
+ self.force_needle = force_needle
23
+ self.security_threshold = security_threshold
24
+ self.routes: Dict[str, RouteDefinition] = {}
25
+
26
+ def register(self, route_def: RouteDefinition) -> "TypeSafeRouter":
27
+ self.routes[route_def.route] = route_def
28
+ return self
29
+
30
+ def route(
31
+ self,
32
+ name: str,
33
+ description: str,
34
+ intent: str,
35
+ enum_fields: Optional[Dict[str, Dict[str, str]]] = None,
36
+ ) -> Callable[[RouteHandler], RouteHandler]:
37
+ """
38
+ Decorator to register a route handler cleanly:
39
+ @router.route(name="create_invoice", description="...", intent="...")
40
+ def handler(payload, ctx):
41
+ ...
42
+ """
43
+
44
+ def decorator(fn: RouteHandler) -> RouteHandler:
45
+ self.register(
46
+ RouteDefinition(
47
+ route=name,
48
+ description=description,
49
+ intent_criteria=intent,
50
+ handler=fn,
51
+ enum_fields=enum_fields,
52
+ )
53
+ )
54
+ return fn
55
+
56
+ return decorator
57
+
58
+ def get_route(self, route_name: str) -> Optional[RouteDefinition]:
59
+ return self.routes.get(route_name)
60
+
61
+ def get_routes(self) -> List[RouteDefinition]:
62
+ return list(self.routes.values())
63
+
64
+ async def _invoke_handler(
65
+ self, handler: RouteHandler, payload: Dict[str, Any], ctx: JITRequestContext
66
+ ) -> Any:
67
+ if inspect.iscoroutinefunction(handler):
68
+ return await handler(payload, ctx)
69
+ return handler(payload, ctx)
70
+
71
+ async def handle(
72
+ self, raw_input: Dict[str, Any], preferred_route: Optional[str] = None
73
+ ) -> Dict[str, Any]:
74
+ start_time = time.time()
75
+
76
+ if not self.routes:
77
+ raise RuntimeError("[TypeSafeRouter] No routes registered.")
78
+
79
+ use_needle = self.force_needle or (
80
+ not self.client.has_api_key() and self.fallback_to_needle
81
+ )
82
+
83
+ # -------------------------------------------------------------
84
+ # Branch A: Needle Local In-Process SLM Fallback
85
+ # -------------------------------------------------------------
86
+ if use_needle:
87
+ routes_list = list(self.routes.values())
88
+ needle_res = self.needle_engine.execute(
89
+ raw_input, routes_list, preferred_route
90
+ )
91
+ matched_route = needle_res["route"]
92
+ route_def = self.routes.get(matched_route)
93
+ if not route_def:
94
+ raise RuntimeError(
95
+ f"[TypeSafeRouter] Matched route '{matched_route}' not found in registry."
96
+ )
97
+
98
+ exec_time_ms = round((time.time() - start_time) * 1000, 2)
99
+ ctx = JITRequestContext(
100
+ route=matched_route,
101
+ phase="phase1_dynamic",
102
+ execution_time_ms=exec_time_ms,
103
+ ai_latency_ms=needle_res["latency_ms"],
104
+ intent_confidence=needle_res["confidence"],
105
+ engine_used="needle",
106
+ )
107
+
108
+ result = await self._invoke_handler(
109
+ route_def.handler, needle_res["normalized_payload"], ctx
110
+ )
111
+
112
+ return {
113
+ "route": matched_route,
114
+ "normalized_payload": needle_res["normalized_payload"],
115
+ "result": result,
116
+ "context": ctx,
117
+ }
118
+
119
+ # -------------------------------------------------------------
120
+ # Branch B: TypeSafe Jev Cloud API (Choice + Select + Noul)
121
+ # -------------------------------------------------------------
122
+ questions = {
123
+ "is_malicious": TypeSafeClient.noul(
124
+ "The input payload contains malicious injection attacks, SQLi, code execution, or security exploits"
125
+ )
126
+ }
127
+
128
+ if not preferred_route or preferred_route not in self.routes:
129
+ criteria = {
130
+ r.route: r.intent_criteria or r.description
131
+ for r in self.routes.values()
132
+ }
133
+ questions["intent"] = TypeSafeClient.choice(criteria)
134
+
135
+ res_data = self.client.system_one(state=raw_input, questions=questions)
136
+ response = res_data["response"]
137
+ latency_ms = res_data["latency_ms"]
138
+ answers = response.get("answers", {})
139
+
140
+ # Security check
141
+ malicious_ans = answers.get("is_malicious")
142
+ if malicious_ans and malicious_ans.get("noul", 0) >= self.security_threshold:
143
+ risk = malicious_ans.get("noul", 0)
144
+ raise PermissionError(
145
+ f"[TypeSafeRouter] Request blocked by Noul security guardrail (Risk score: {risk:.2f} >= {self.security_threshold})"
146
+ )
147
+
148
+ # Route decision
149
+ matched_route = preferred_route
150
+ confidence = 1.0
151
+
152
+ if not matched_route or matched_route not in self.routes:
153
+ intent_ans = answers.get("intent", {})
154
+ matched_route = intent_ans.get("choice")
155
+ confidence = intent_ans.get("confidence", 1.0)
156
+
157
+ route_def = self.routes.get(matched_route)
158
+ if not route_def:
159
+ raise RuntimeError(
160
+ f"[TypeSafeRouter] Matched route '{matched_route}' not found in registry."
161
+ )
162
+
163
+ # Enum Extraction
164
+ normalized_payload = dict(raw_input)
165
+ if route_def.enum_fields:
166
+ enum_q = {
167
+ field: TypeSafeClient.choice(opts)
168
+ for field, opts in route_def.enum_fields.items()
169
+ }
170
+ enum_res = self.client.system_one(state=raw_input, questions=enum_q)
171
+ for f_name, f_ans in enum_res.get("response", {}).get("answers", {}).items():
172
+ if f_ans.get("type") == "choice":
173
+ normalized_payload[f_name] = f_ans.get("choice")
174
+
175
+ exec_time_ms = round((time.time() - start_time) * 1000, 2)
176
+ ctx = JITRequestContext(
177
+ route=matched_route,
178
+ phase="phase1_dynamic",
179
+ execution_time_ms=exec_time_ms,
180
+ ai_latency_ms=latency_ms,
181
+ intent_confidence=confidence,
182
+ engine_used="typesafe",
183
+ )
184
+
185
+ result = await self._invoke_handler(
186
+ route_def.handler, normalized_payload, ctx
187
+ )
188
+
189
+ return {
190
+ "route": matched_route,
191
+ "normalized_payload": normalized_payload,
192
+ "result": result,
193
+ "context": ctx,
194
+ }
jit_api/types.py ADDED
@@ -0,0 +1,69 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import Any, Callable, Coroutine, Dict, List, Literal, Optional, Union
3
+
4
+ LifecyclePhase = Literal["phase1_dynamic", "phase2_observing", "phase3_frozen"]
5
+ IRFieldType = Literal["string", "number", "boolean", "enum", "array", "object"]
6
+
7
+
8
+ @dataclass
9
+ class IRField:
10
+ name: str
11
+ type: IRFieldType
12
+ required: bool = True
13
+ enum_values: Optional[List[str]] = None
14
+ description: Optional[str] = None
15
+ item_type: Optional[IRFieldType] = None
16
+ properties: Optional[Dict[str, "IRField"]] = None
17
+
18
+
19
+ @dataclass
20
+ class IRSchema:
21
+ name: str
22
+ version: int
23
+ route: str
24
+ fields: Dict[str, IRField]
25
+ description: Optional[str] = None
26
+ sample_payload: Optional[Dict[str, Any]] = None
27
+ frozen_at: Optional[str] = None
28
+
29
+
30
+ @dataclass
31
+ class JITRequestContext:
32
+ route: str
33
+ phase: LifecyclePhase
34
+ execution_time_ms: float
35
+ ai_latency_ms: float
36
+ intent_confidence: float = 1.0
37
+ is_fallback: bool = False
38
+ engine_used: Optional[Literal["typesafe", "needle"]] = None
39
+
40
+
41
+ @dataclass
42
+ class JITExecutionResult:
43
+ success: bool
44
+ data: Any = None
45
+ error: Optional[str] = None
46
+ context: Optional[JITRequestContext] = None
47
+
48
+
49
+ @dataclass
50
+ class StabilityMetrics:
51
+ sample_count: int
52
+ consecutive_matches: int
53
+ required_threshold: int
54
+ confidence_threshold: float
55
+ avg_confidence: float
56
+ is_stable: bool
57
+
58
+
59
+ # Route handler can be async or sync
60
+ RouteHandler = Callable[[Dict[str, Any], JITRequestContext], Union[Any, Coroutine[Any, Any, Any]]]
61
+
62
+
63
+ @dataclass
64
+ class RouteDefinition:
65
+ route: str
66
+ description: str
67
+ intent_criteria: str
68
+ handler: RouteHandler
69
+ enum_fields: Optional[Dict[str, Dict[str, str]]] = None
@@ -0,0 +1,75 @@
1
+ import os
2
+ import time
3
+ from typing import Any, Dict, Optional
4
+ from dotenv import load_dotenv
5
+ import requests
6
+
7
+ load_dotenv()
8
+
9
+
10
+ class TypeSafeClient:
11
+ def __init__(
12
+ self,
13
+ api_key: Optional[str] = None,
14
+ base_url: Optional[str] = None,
15
+ model: Optional[str] = None,
16
+ timeout_sec: float = 10.0,
17
+ ):
18
+ self.api_key = (
19
+ api_key if api_key is not None else os.getenv("TYPESAFE_API_KEY", "")
20
+ )
21
+ self.base_url = base_url or "https://api.typesafe.ai/v1/systemone"
22
+ self.default_model = model or "jev-latest"
23
+ self.timeout_sec = timeout_sec
24
+
25
+ if not self.has_api_key():
26
+ # Will trigger Needle fallback automatically
27
+ pass
28
+
29
+ def has_api_key(self) -> bool:
30
+ return bool(self.api_key and self.api_key.strip())
31
+
32
+ @staticmethod
33
+ def choice(criteria: Dict[str, str]) -> Dict[str, Any]:
34
+ return {"type": "choice", "criteria": criteria}
35
+
36
+ @staticmethod
37
+ def noul(instructions: str) -> Dict[str, Any]:
38
+ return {"type": "noul", "instructions": instructions}
39
+
40
+ @staticmethod
41
+ def score(levels: Dict[str, str]) -> Dict[str, Any]:
42
+ return {"type": "score", "levels": levels}
43
+
44
+ def system_one(
45
+ self,
46
+ state: Dict[str, Any],
47
+ questions: Dict[str, Any],
48
+ model: Optional[str] = None,
49
+ ) -> Dict[str, Any]:
50
+ start_time = time.time()
51
+ payload = {
52
+ "model": model or self.default_model,
53
+ "state": state,
54
+ "questions": questions,
55
+ }
56
+
57
+ headers = {
58
+ "Content-Type": "application/json",
59
+ "Authorization": f"Bearer {self.api_key}",
60
+ }
61
+
62
+ resp = requests.post(
63
+ self.base_url,
64
+ json=payload,
65
+ headers=headers,
66
+ timeout=self.timeout_sec,
67
+ )
68
+ latency_ms = round((time.time() - start_time) * 1000, 2)
69
+
70
+ if not resp.ok:
71
+ raise RuntimeError(
72
+ f"TypeSafe Jev API error ({resp.status_code}): {resp.text}"
73
+ )
74
+
75
+ return {"response": resp.json(), "latency_ms": latency_ms}
@@ -0,0 +1,92 @@
1
+ Metadata-Version: 2.4
2
+ Name: jit-protocol
3
+ Version: 1.0.0
4
+ Summary: Just-In-Time Protocol Synthesis Framework: Dynamic-to-Static API Generation using TypeSafe (Jev) and Needle
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: pydantic>=2.0.0
8
+ Requires-Dist: requests>=2.28.0
9
+ Requires-Dist: python-dotenv>=1.0.0
10
+ Requires-Dist: cactus-needle>=3.0.0
11
+ Provides-Extra: fastapi
12
+ Requires-Dist: fastapi>=0.100.0; extra == "fastapi"
13
+ Requires-Dist: uvicorn>=0.20.0; extra == "fastapi"
14
+ Provides-Extra: test
15
+ Requires-Dist: pytest>=7.0.0; extra == "test"
16
+ Requires-Dist: pytest-asyncio>=0.20.0; extra == "test"
17
+
18
+ # JIT Protocol Synthesis Framework - Python Engine (`jit-api-python`)
19
+
20
+ > **基於 TypeSafe (Jev) 與 Cactus Needle 3.0 的原生 Python 動態至靜態 API 合成引擎**
21
+ > *Pythonic Native Host Engine for Just-In-Time Protocol Synthesis*
22
+
23
+ ---
24
+
25
+ ## 🌟 特色
26
+
27
+ 1. **同進程 Cactus Needle 3.0 端側推論**:
28
+ 在無 `TYPESAFE_API_KEY` 或離線狀態下,直接調用進程內的 Needle 3 SAN (2-bit, 35MB) 進行毫秒級 Tool Calling 與結構萃取,**0 網路轉發延遲、0 API 費用**。
29
+ 2. **極致優雅的裝飾器語法**:
30
+ 使用 `@engine.route(...)` 宣告語意意圖,無縫支援 `async def` 與同步 `def` 函式。
31
+ 3. **Pydantic v2 動態模型極速路徑**:
32
+ 當路由結構在過去 N 次請求中穩定後,自動生成動態 Pydantic v2 BaseModel,Phase 3 靜態驗證僅需 **0.03ms (0ms AI 延遲)**!
33
+ 4. **自動版本演進與降級保護**:
34
+ 若遇到型態漂移(Schema Drift),自動降級回 Phase 1 並開啟 v2 / v3 版本的觀測與合約重新凍結。
35
+ 5. **多語言程式碼生成積木**:
36
+ 凍結時同步輸出 TypeScript (Zod)、Golang (.proto / struct) 與 Python (Pydantic / FastAPI)。
37
+
38
+ ---
39
+
40
+ ## 🚀 快速上手 (Quick Start)
41
+
42
+ ### 執行環境 (Conda `toby`)
43
+ ```bash
44
+ conda activate toby
45
+ ```
46
+
47
+ ### 宣告端點與執行
48
+ ```python
49
+ import asyncio
50
+ from jit_api import JITEngine
51
+
52
+ engine = JITEngine(stability_threshold=3)
53
+
54
+ @engine.route(
55
+ name="create_invoice",
56
+ description="Issue a customer billing invoice",
57
+ intent="Bill or invoice a customer for items",
58
+ enum_fields={"currency": {"USD": "US Dollar", "EUR": "Euro"}}
59
+ )
60
+ async def create_invoice(payload, ctx):
61
+ return {
62
+ "invoice_id": "INV-100",
63
+ "customer": payload.get("customer"),
64
+ "amount": payload.get("amount"),
65
+ "phase": ctx.phase,
66
+ }
67
+
68
+ async def main():
69
+ # 支援自然語言或鬆散 JSON
70
+ res = await engine.execute({
71
+ "message": "Please bill Wayne Corp for 500 dollars urgently",
72
+ "customer": "Wayne Corp",
73
+ "amount": 500,
74
+ "currency": "USD"
75
+ })
76
+ print(res.data)
77
+
78
+ if __name__ == "__main__":
79
+ asyncio.run(main())
80
+ ```
81
+
82
+ ---
83
+
84
+ ## 🧪 執行測試與演示
85
+
86
+ ```bash
87
+ # 執行全套 pytest 測試
88
+ PYTHONPATH=python /home/toby/miniconda3/envs/toby/bin/pytest python/tests/ -v
89
+
90
+ # 執行端到端生命週期演示
91
+ PYTHONPATH=python /home/toby/miniconda3/envs/toby/bin/python python/examples/demo_lifecycle.py
92
+ ```
@@ -0,0 +1,13 @@
1
+ jit_api/__init__.py,sha256=vP1f_zvwGTGUwcltgPmSYRPxvDLLmezjB8W7edXlB5U,933
2
+ jit_api/codegen.py,sha256=e_InQU3Zikive9olRR0ewEmiVXLAUxnNVG_ZEVzioyM,6179
3
+ jit_api/engine.py,sha256=L6H3gT8XTFuvLQQrpWdtQfSNY_mLhYES-2igMsXjzvo,7745
4
+ jit_api/fallback_handler.py,sha256=GZ0FHg0HxTDVUq9hBttttAn12aLmL_U03GLUzHeLIys,1777
5
+ jit_api/needle_engine.py,sha256=IMmTDuqYZuFUsESWt8LHEotLFHp1GJhstskyx1ao7ds,5594
6
+ jit_api/observer.py,sha256=5Z4KJS1odGXwdCs0SgYy8H3MCOvHyzsPHBsAPQIlcTI,6558
7
+ jit_api/router.py,sha256=ANFPGKysC5e0GYj3U0Aj6ArsFrgw02OfDmmdsaFYxuQ,7119
8
+ jit_api/types.py,sha256=e-wx0VjwTmVYRIJ-d0OLu8JFOYICZDkH8sciexc5ufw,1721
9
+ jit_api/typesafe_client.py,sha256=8oCoH47o96yPpe5Jl_PF-GdX7Vg2nwrrMLMgiIHkFDg,2133
10
+ jit_protocol-1.0.0.dist-info/METADATA,sha256=Rh67Qt7yB9dMv3sc-Zl9AYU-DdwiALUsbxwFe2SUQ6E,3055
11
+ jit_protocol-1.0.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
12
+ jit_protocol-1.0.0.dist-info/top_level.txt,sha256=9Q3BcLP9-M-McZx7hdVi5kCLXMzDuikk6IpXm4HKrtg,8
13
+ jit_protocol-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ jit_api