tokenpak 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.
Files changed (450) hide show
  1. tokenpak/__init__.py +171 -0
  2. tokenpak/__main__.py +7 -0
  3. tokenpak/adapters/__init__.py +51 -0
  4. tokenpak/adapters/anthropic.py +258 -0
  5. tokenpak/adapters/base.py +212 -0
  6. tokenpak/adapters/langchain.py +174 -0
  7. tokenpak/adapters/litellm.py +192 -0
  8. tokenpak/adapters/openai.py +251 -0
  9. tokenpak/agent/__init__.py +1 -0
  10. tokenpak/agent/adapters/__init__.py +27 -0
  11. tokenpak/agent/adapters/base.py +59 -0
  12. tokenpak/agent/adapters/claude_cli.py +50 -0
  13. tokenpak/agent/adapters/generic.py +41 -0
  14. tokenpak/agent/adapters/openclaw.py +50 -0
  15. tokenpak/agent/adapters/registry.py +56 -0
  16. tokenpak/agent/agentic/__init__.py +154 -0
  17. tokenpak/agent/agentic/capabilities.py +269 -0
  18. tokenpak/agent/agentic/error_normalizer.py +156 -0
  19. tokenpak/agent/agentic/failure_memory.py +357 -0
  20. tokenpak/agent/agentic/handoff.py +517 -0
  21. tokenpak/agent/agentic/learning.py +894 -0
  22. tokenpak/agent/agentic/locks.py +248 -0
  23. tokenpak/agent/agentic/memory_promoter.py +304 -0
  24. tokenpak/agent/agentic/precondition_gates.py +432 -0
  25. tokenpak/agent/agentic/prefetcher.py +175 -0
  26. tokenpak/agent/agentic/proxy_workflow.py +167 -0
  27. tokenpak/agent/agentic/registry.py +211 -0
  28. tokenpak/agent/agentic/retry.py +528 -0
  29. tokenpak/agent/agentic/runbook_generator.py +446 -0
  30. tokenpak/agent/agentic/skill_compiler.py +360 -0
  31. tokenpak/agent/agentic/state_collector.py +389 -0
  32. tokenpak/agent/agentic/test_workflow_performance.py +360 -0
  33. tokenpak/agent/agentic/validation_framework.py +723 -0
  34. tokenpak/agent/agentic/workflow.py +457 -0
  35. tokenpak/agent/agentic/workflow_budget.py +363 -0
  36. tokenpak/agent/agentic/workflow_performance.py +285 -0
  37. tokenpak/agent/auth/__init__.py +0 -0
  38. tokenpak/agent/auth/cooldown_manager.py +246 -0
  39. tokenpak/agent/auth/oauth_manager.py +368 -0
  40. tokenpak/agent/cli/__init__.py +9 -0
  41. tokenpak/agent/cli/commands/__init__.py +23 -0
  42. tokenpak/agent/cli/commands/budget.py +633 -0
  43. tokenpak/agent/cli/commands/compliance.py +134 -0
  44. tokenpak/agent/cli/commands/compression.py +21 -0
  45. tokenpak/agent/cli/commands/config.py +123 -0
  46. tokenpak/agent/cli/commands/cost.py +403 -0
  47. tokenpak/agent/cli/commands/dashboard.py +412 -0
  48. tokenpak/agent/cli/commands/debug.py +52 -0
  49. tokenpak/agent/cli/commands/diff.py +303 -0
  50. tokenpak/agent/cli/commands/doctor.py +480 -0
  51. tokenpak/agent/cli/commands/exec.py +198 -0
  52. tokenpak/agent/cli/commands/fingerprint.py +209 -0
  53. tokenpak/agent/cli/commands/handoff.py +231 -0
  54. tokenpak/agent/cli/commands/help.py +324 -0
  55. tokenpak/agent/cli/commands/index.py +111 -0
  56. tokenpak/agent/cli/commands/last.py +119 -0
  57. tokenpak/agent/cli/commands/license.py +121 -0
  58. tokenpak/agent/cli/commands/maintenance.py +51 -0
  59. tokenpak/agent/cli/commands/metrics.py +233 -0
  60. tokenpak/agent/cli/commands/optimize.py +528 -0
  61. tokenpak/agent/cli/commands/policy.py +172 -0
  62. tokenpak/agent/cli/commands/preview.py +136 -0
  63. tokenpak/agent/cli/commands/prune.py +232 -0
  64. tokenpak/agent/cli/commands/replay.py +263 -0
  65. tokenpak/agent/cli/commands/retain.py +219 -0
  66. tokenpak/agent/cli/commands/route.py +36 -0
  67. tokenpak/agent/cli/commands/savings.py +319 -0
  68. tokenpak/agent/cli/commands/serve.py +99 -0
  69. tokenpak/agent/cli/commands/sla.py +147 -0
  70. tokenpak/agent/cli/commands/status.py +174 -0
  71. tokenpak/agent/cli/commands/teacher.py +68 -0
  72. tokenpak/agent/cli/commands/template.py +204 -0
  73. tokenpak/agent/cli/commands/trigger.py +7 -0
  74. tokenpak/agent/cli/commands/vault.py +32 -0
  75. tokenpak/agent/cli/commands/workflow.py +428 -0
  76. tokenpak/agent/cli/main.py +855 -0
  77. tokenpak/agent/cli/trigger_cmd.py +330 -0
  78. tokenpak/agent/compression/__init__.py +91 -0
  79. tokenpak/agent/compression/alias_compressor.py +309 -0
  80. tokenpak/agent/compression/canon.py +455 -0
  81. tokenpak/agent/compression/dedup.py +178 -0
  82. tokenpak/agent/compression/dictionary.py +307 -0
  83. tokenpak/agent/compression/directives.py +647 -0
  84. tokenpak/agent/compression/fidelity_tiers.py +545 -0
  85. tokenpak/agent/compression/instruction_table.py +219 -0
  86. tokenpak/agent/compression/pipeline.py +205 -0
  87. tokenpak/agent/compression/query_rewriter.py +365 -0
  88. tokenpak/agent/compression/recipes.py +637 -0
  89. tokenpak/agent/compression/salience/__init__.py +34 -0
  90. tokenpak/agent/compression/salience/code_extractor.py +269 -0
  91. tokenpak/agent/compression/salience/detect.py +70 -0
  92. tokenpak/agent/compression/salience/doc_extractor.py +147 -0
  93. tokenpak/agent/compression/salience/log_extractor.py +196 -0
  94. tokenpak/agent/compression/salience/router.py +128 -0
  95. tokenpak/agent/compression/schema_extractor.py +530 -0
  96. tokenpak/agent/compression/segmentizer.py +594 -0
  97. tokenpak/agent/compression/slot_filler.py +293 -0
  98. tokenpak/agent/config.py +166 -0
  99. tokenpak/agent/dashboard/__init__.py +6 -0
  100. tokenpak/agent/dashboard/export_api.py +120 -0
  101. tokenpak/agent/dashboard/export_csv.py +187 -0
  102. tokenpak/agent/dashboard/session_filter.py +260 -0
  103. tokenpak/agent/debug/__init__.py +6 -0
  104. tokenpak/agent/debug/logger.py +61 -0
  105. tokenpak/agent/debug/state.py +90 -0
  106. tokenpak/agent/fingerprint/__init__.py +22 -0
  107. tokenpak/agent/fingerprint/generator.py +207 -0
  108. tokenpak/agent/fingerprint/privacy.py +44 -0
  109. tokenpak/agent/fingerprint/sync.py +372 -0
  110. tokenpak/agent/ingest/__init__.py +37 -0
  111. tokenpak/agent/ingest/api.py +176 -0
  112. tokenpak/agent/ingest/claim_indexer.py +276 -0
  113. tokenpak/agent/ingest/cross_doc.py +784 -0
  114. tokenpak/agent/ingest/disclosure.py +183 -0
  115. tokenpak/agent/ingest/document_parser.py +861 -0
  116. tokenpak/agent/ingest/schema_converter.py +134 -0
  117. tokenpak/agent/ingest/table_extractor.py +464 -0
  118. tokenpak/agent/ingest/test_document_parser.py +400 -0
  119. tokenpak/agent/license/__init__.py +23 -0
  120. tokenpak/agent/license/activation.py +244 -0
  121. tokenpak/agent/license/admin_cli.py +267 -0
  122. tokenpak/agent/license/keys.py +188 -0
  123. tokenpak/agent/license/store.py +158 -0
  124. tokenpak/agent/license/validator.py +301 -0
  125. tokenpak/agent/macros/__init__.py +104 -0
  126. tokenpak/agent/macros/engine.py +550 -0
  127. tokenpak/agent/macros/hooks.py +454 -0
  128. tokenpak/agent/macros/premade_macros.py +225 -0
  129. tokenpak/agent/macros/scheduler.py +250 -0
  130. tokenpak/agent/macros/script_hooks.py +235 -0
  131. tokenpak/agent/memory/__init__.py +17 -0
  132. tokenpak/agent/memory/session_capsules.py +150 -0
  133. tokenpak/agent/proxy/__init__.py +35 -0
  134. tokenpak/agent/proxy/capsule_builder.py +80 -0
  135. tokenpak/agent/proxy/capsule_integration.py +242 -0
  136. tokenpak/agent/proxy/circuit_breaker.py +349 -0
  137. tokenpak/agent/proxy/connection_pool.py +448 -0
  138. tokenpak/agent/proxy/degradation.py +183 -0
  139. tokenpak/agent/proxy/example_selector.py +139 -0
  140. tokenpak/agent/proxy/failover.py +301 -0
  141. tokenpak/agent/proxy/failover_engine.py +642 -0
  142. tokenpak/agent/proxy/intent_policy.py +551 -0
  143. tokenpak/agent/proxy/oauth.py +274 -0
  144. tokenpak/agent/proxy/passthrough.py +273 -0
  145. tokenpak/agent/proxy/prompt_builder.py +1088 -0
  146. tokenpak/agent/proxy/providers/__init__.py +22 -0
  147. tokenpak/agent/proxy/providers/anthropic.py +229 -0
  148. tokenpak/agent/proxy/providers/detector.py +183 -0
  149. tokenpak/agent/proxy/providers/google.py +144 -0
  150. tokenpak/agent/proxy/providers/openai.py +164 -0
  151. tokenpak/agent/proxy/providers/stream_translator.py +540 -0
  152. tokenpak/agent/proxy/providers/translator.py +784 -0
  153. tokenpak/agent/proxy/proxy.py +62 -0
  154. tokenpak/agent/proxy/router.py +308 -0
  155. tokenpak/agent/proxy/server.py +1553 -0
  156. tokenpak/agent/proxy/server_async.py +1006 -0
  157. tokenpak/agent/proxy/startup.py +131 -0
  158. tokenpak/agent/proxy/stats.py +240 -0
  159. tokenpak/agent/proxy/stats_api.py +49 -0
  160. tokenpak/agent/proxy/streaming.py +181 -0
  161. tokenpak/agent/proxy/test_prompt_pack.py +352 -0
  162. tokenpak/agent/proxy/tool_schema_registry.py +242 -0
  163. tokenpak/agent/query/__init__.py +1 -0
  164. tokenpak/agent/query/api.py +408 -0
  165. tokenpak/agent/recipe_sdk.py +687 -0
  166. tokenpak/agent/regression/__init__.py +58 -0
  167. tokenpak/agent/regression/artifact_reuse.py +161 -0
  168. tokenpak/agent/regression/baseline_registry.py +95 -0
  169. tokenpak/agent/regression/classifier.py +119 -0
  170. tokenpak/agent/regression/delta_detector.py +189 -0
  171. tokenpak/agent/regression/feature_detector.py +168 -0
  172. tokenpak/agent/regression/retrieval_watchdog.py +437 -0
  173. tokenpak/agent/regression/stability_scorer.py +330 -0
  174. tokenpak/agent/regression/tests/test_classifier.py +86 -0
  175. tokenpak/agent/routing/__init__.py +1 -0
  176. tokenpak/agent/routing/fallback.py +248 -0
  177. tokenpak/agent/routing/rules.py +24 -0
  178. tokenpak/agent/semantic/__init__.py +38 -0
  179. tokenpak/agent/semantic/term_card_builder.py +492 -0
  180. tokenpak/agent/semantic/term_resolver.py +343 -0
  181. tokenpak/agent/semantic/test_proxy_integration.py +342 -0
  182. tokenpak/agent/semantic/test_term_resolver.py +438 -0
  183. tokenpak/agent/state_schemas/__init__.py +37 -0
  184. tokenpak/agent/teacher/__init__.py +5 -0
  185. tokenpak/agent/teacher/builder.py +217 -0
  186. tokenpak/agent/team/__init__.py +17 -0
  187. tokenpak/agent/team/agent_registry.py +223 -0
  188. tokenpak/agent/team/shared_vault.py +221 -0
  189. tokenpak/agent/team/templates.py +242 -0
  190. tokenpak/agent/telemetry/__init__.py +35 -0
  191. tokenpak/agent/telemetry/budget.py +389 -0
  192. tokenpak/agent/telemetry/collector.py +194 -0
  193. tokenpak/agent/telemetry/cost_tracker.py +256 -0
  194. tokenpak/agent/telemetry/demo.py +40 -0
  195. tokenpak/agent/telemetry/footer.py +103 -0
  196. tokenpak/agent/telemetry/replay.py +299 -0
  197. tokenpak/agent/telemetry/storage.py +154 -0
  198. tokenpak/agent/triggers/__init__.py +7 -0
  199. tokenpak/agent/triggers/daemon.py +192 -0
  200. tokenpak/agent/triggers/matcher.py +58 -0
  201. tokenpak/agent/triggers/store.py +103 -0
  202. tokenpak/agent/vault/__init__.py +27 -0
  203. tokenpak/agent/vault/ast_parser.py +245 -0
  204. tokenpak/agent/vault/blocks.py +290 -0
  205. tokenpak/agent/vault/chunk_shapes.py +376 -0
  206. tokenpak/agent/vault/indexer.py +202 -0
  207. tokenpak/agent/vault/retrieval.py +524 -0
  208. tokenpak/agent/vault/scoring.py +256 -0
  209. tokenpak/agent/vault/slicer.py +263 -0
  210. tokenpak/agent/vault/sqlite_retrieval.py +530 -0
  211. tokenpak/agent/vault/symbols.py +168 -0
  212. tokenpak/agent/vault/watcher.py +338 -0
  213. tokenpak/alerts.py +295 -0
  214. tokenpak/api/__init__.py +10 -0
  215. tokenpak/api/routes.py +185 -0
  216. tokenpak/artifact_store.py +431 -0
  217. tokenpak/assembler.py +337 -0
  218. tokenpak/attribution.py +263 -0
  219. tokenpak/benchmark.py +1405 -0
  220. tokenpak/broker.py +298 -0
  221. tokenpak/budget.py +90 -0
  222. tokenpak/budget_controller.py +149 -0
  223. tokenpak/budgeter.py +417 -0
  224. tokenpak/cache/__init__.py +72 -0
  225. tokenpak/cache/prefix_registry.py +224 -0
  226. tokenpak/cache/registry.py +121 -0
  227. tokenpak/cache/semantic_cache.py +341 -0
  228. tokenpak/cache/stable_cache.py +125 -0
  229. tokenpak/cache/telemetry.py +356 -0
  230. tokenpak/cache/volatile_cache.py +122 -0
  231. tokenpak/cache_report.py +50 -0
  232. tokenpak/calibration.py +187 -0
  233. tokenpak/calibrator.py +346 -0
  234. tokenpak/capsule/__init__.py +15 -0
  235. tokenpak/capsule/builder.py +362 -0
  236. tokenpak/citation_tracker.py +200 -0
  237. tokenpak/cli.py +5533 -0
  238. tokenpak/cli_doctor.py +198 -0
  239. tokenpak/compaction/__init__.py +54 -0
  240. tokenpak/compaction/modes.py +229 -0
  241. tokenpak/compaction/policy.py +282 -0
  242. tokenpak/compaction/topic_aware.py +345 -0
  243. tokenpak/compiler.py +207 -0
  244. tokenpak/complexity.py +309 -0
  245. tokenpak/config_loader.py +265 -0
  246. tokenpak/config_validator.py +294 -0
  247. tokenpak/connectors/__init__.py +42 -0
  248. tokenpak/connectors/base.py +95 -0
  249. tokenpak/connectors/base_source.py +81 -0
  250. tokenpak/connectors/git_adapter.py +110 -0
  251. tokenpak/connectors/github.py +174 -0
  252. tokenpak/connectors/google_drive.py +133 -0
  253. tokenpak/connectors/local.py +70 -0
  254. tokenpak/connectors/notion.py +181 -0
  255. tokenpak/connectors/notion_adapter.py +186 -0
  256. tokenpak/connectors/obsidian.py +109 -0
  257. tokenpak/connectors/url_adapter.py +153 -0
  258. tokenpak/context_composer.py +274 -0
  259. tokenpak/core.py +342 -0
  260. tokenpak/daily_report.py +212 -0
  261. tokenpak/dashboard/__init__.py +47 -0
  262. tokenpak/elo.py +164 -0
  263. tokenpak/engines/__init__.py +28 -0
  264. tokenpak/engines/base.py +42 -0
  265. tokenpak/engines/heuristic.py +45 -0
  266. tokenpak/engines/llmlingua.py +80 -0
  267. tokenpak/enterprise/__init__.py +10 -0
  268. tokenpak/enterprise/audit.py +413 -0
  269. tokenpak/enterprise/compliance.py +529 -0
  270. tokenpak/enterprise/governance.py +240 -0
  271. tokenpak/enterprise/policy.py +211 -0
  272. tokenpak/enterprise/sla.py +228 -0
  273. tokenpak/escalation.py +122 -0
  274. tokenpak/evidence_pack.py +236 -0
  275. tokenpak/extraction/__init__.py +13 -0
  276. tokenpak/extraction/extractor.py +161 -0
  277. tokenpak/extraction/models.py +88 -0
  278. tokenpak/extraction/patterns.py +41 -0
  279. tokenpak/fleet.py +282 -0
  280. tokenpak/formatting/__init__.py +4 -0
  281. tokenpak/formatting/colors.py +33 -0
  282. tokenpak/formatting/formatter.py +56 -0
  283. tokenpak/formatting/modes.py +19 -0
  284. tokenpak/formatting/symbols.py +19 -0
  285. tokenpak/goals.py +492 -0
  286. tokenpak/handlers/__init__.py +1 -0
  287. tokenpak/handlers/rate_limit.py +73 -0
  288. tokenpak/integrations/__init__.py +1 -0
  289. tokenpak/integrations/litellm/__init__.py +38 -0
  290. tokenpak/integrations/litellm/formatter.py +158 -0
  291. tokenpak/integrations/litellm/middleware.py +182 -0
  292. tokenpak/integrations/litellm/parser.py +76 -0
  293. tokenpak/integrations/litellm/proxy.py +156 -0
  294. tokenpak/intelligence/__init__.py +1 -0
  295. tokenpak/intelligence/ab_optimizer.py +631 -0
  296. tokenpak/intelligence/ab_router.py +244 -0
  297. tokenpak/intelligence/auth.py +313 -0
  298. tokenpak/intelligence/cost_intelligence.py +435 -0
  299. tokenpak/intelligence/cost_router.py +264 -0
  300. tokenpak/intelligence/deep_health.py +369 -0
  301. tokenpak/intelligence/license_endpoint.py +156 -0
  302. tokenpak/intelligence/server.py +366 -0
  303. tokenpak/intent_classifier.py +434 -0
  304. tokenpak/middleware/__init__.py +56 -0
  305. tokenpak/middleware/audit_trail.py +175 -0
  306. tokenpak/middleware/logger.py +237 -0
  307. tokenpak/middleware/logging_middleware.py +209 -0
  308. tokenpak/middleware/semantic_cache_middleware.py +153 -0
  309. tokenpak/middleware/tests/__init__.py +0 -0
  310. tokenpak/middleware/tests/conftest.py +10 -0
  311. tokenpak/middleware/tests/test_audit_trail.py +220 -0
  312. tokenpak/middleware/tests/test_logger.py +294 -0
  313. tokenpak/middleware/tests/test_logging_middleware.py +138 -0
  314. tokenpak/miss_detector.py +461 -0
  315. tokenpak/monitoring/__init__.py +21 -0
  316. tokenpak/monitoring/audit_trail.py +165 -0
  317. tokenpak/monitoring/health.py +217 -0
  318. tokenpak/monitoring/metrics.py +386 -0
  319. tokenpak/monitoring/request_logger.py +517 -0
  320. tokenpak/pack.py +455 -0
  321. tokenpak/post_run.py +268 -0
  322. tokenpak/precompute.py +634 -0
  323. tokenpak/pricing.py +143 -0
  324. tokenpak/processors/__init__.py +38 -0
  325. tokenpak/processors/code.py +474 -0
  326. tokenpak/processors/code_treesitter.py +532 -0
  327. tokenpak/processors/data.py +137 -0
  328. tokenpak/processors/text.py +195 -0
  329. tokenpak/profiles.py +129 -0
  330. tokenpak/proxy/__init__.py +3 -0
  331. tokenpak/proxy/adapters/__init__.py +34 -0
  332. tokenpak/proxy/adapters/anthropic_adapter.py +115 -0
  333. tokenpak/proxy/adapters/base.py +179 -0
  334. tokenpak/proxy/adapters/canonical.py +34 -0
  335. tokenpak/proxy/adapters/google_adapter.py +129 -0
  336. tokenpak/proxy/adapters/openai_chat_adapter.py +89 -0
  337. tokenpak/proxy/adapters/openai_responses_adapter.py +114 -0
  338. tokenpak/proxy/adapters/passthrough_adapter.py +76 -0
  339. tokenpak/proxy/adapters/registry.py +42 -0
  340. tokenpak/proxy/credential_passthrough.py +283 -0
  341. tokenpak/py.typed +0 -0
  342. tokenpak/reference_fetcher.py +151 -0
  343. tokenpak/reference_scanner.py +124 -0
  344. tokenpak/registry.py +326 -0
  345. tokenpak/report.py +299 -0
  346. tokenpak/request_audit.py +211 -0
  347. tokenpak/routing/__init__.py +3 -0
  348. tokenpak/routing/rules.py +359 -0
  349. tokenpak/routing_ledger.py +268 -0
  350. tokenpak/schemas/__init__.py +8 -0
  351. tokenpak/schemas/artifact.py +76 -0
  352. tokenpak/schemas/chunk.py +66 -0
  353. tokenpak/schemas/retrieval_cache.py +87 -0
  354. tokenpak/schemas/source_map.py +69 -0
  355. tokenpak/security.py +216 -0
  356. tokenpak/semantic/__init__.py +27 -0
  357. tokenpak/semantic/loader.py +213 -0
  358. tokenpak/semantic/resolver.py +292 -0
  359. tokenpak/shadow_hook.py +157 -0
  360. tokenpak/shadow_reader.py +558 -0
  361. tokenpak/span_extractor.py +227 -0
  362. tokenpak/state_manager.py +388 -0
  363. tokenpak/telemetry/__init__.py +35 -0
  364. tokenpak/telemetry/adapters/__init__.py +33 -0
  365. tokenpak/telemetry/adapters/anthropic.py +199 -0
  366. tokenpak/telemetry/adapters/base.py +93 -0
  367. tokenpak/telemetry/adapters/gemini.py +229 -0
  368. tokenpak/telemetry/adapters/openai.py +231 -0
  369. tokenpak/telemetry/adapters/registry.py +194 -0
  370. tokenpak/telemetry/anon_metrics.py +274 -0
  371. tokenpak/telemetry/api.py +189 -0
  372. tokenpak/telemetry/cache.py +229 -0
  373. tokenpak/telemetry/canonical.py +166 -0
  374. tokenpak/telemetry/collector.py +213 -0
  375. tokenpak/telemetry/config.py +108 -0
  376. tokenpak/telemetry/cost.py +659 -0
  377. tokenpak/telemetry/dashboard/__init__.py +8 -0
  378. tokenpak/telemetry/dashboard/dashboard.py +1053 -0
  379. tokenpak/telemetry/dashboard/pagination.py +127 -0
  380. tokenpak/telemetry/dashboard/query_builder.py +213 -0
  381. tokenpak/telemetry/event_schema.py +365 -0
  382. tokenpak/telemetry/insights.py +511 -0
  383. tokenpak/telemetry/integrity/__init__.py +0 -0
  384. tokenpak/telemetry/integrity/anomalies.py +353 -0
  385. tokenpak/telemetry/integrity/reconciliation.py +209 -0
  386. tokenpak/telemetry/integrity/validation.py +249 -0
  387. tokenpak/telemetry/milestones.py +391 -0
  388. tokenpak/telemetry/models.py +344 -0
  389. tokenpak/telemetry/operational/__init__.py +0 -0
  390. tokenpak/telemetry/operational/api.py +229 -0
  391. tokenpak/telemetry/operational/health.py +176 -0
  392. tokenpak/telemetry/operational/metrics.py +158 -0
  393. tokenpak/telemetry/operational/pruning.py +166 -0
  394. tokenpak/telemetry/pipeline.py +364 -0
  395. tokenpak/telemetry/pipeline_trace.py +106 -0
  396. tokenpak/telemetry/pricing.py +419 -0
  397. tokenpak/telemetry/prometheus.py +371 -0
  398. tokenpak/telemetry/proxy_trace_integration.py +170 -0
  399. tokenpak/telemetry/query.py +259 -0
  400. tokenpak/telemetry/query_models.py +50 -0
  401. tokenpak/telemetry/reporter.py +172 -0
  402. tokenpak/telemetry/response_models.py +259 -0
  403. tokenpak/telemetry/rollups.py +641 -0
  404. tokenpak/telemetry/segmentizer.py +1020 -0
  405. tokenpak/telemetry/server.py +1055 -0
  406. tokenpak/telemetry/settings.py +160 -0
  407. tokenpak/telemetry/stats.py +178 -0
  408. tokenpak/telemetry/storage.py +1314 -0
  409. tokenpak/telemetry/storage_base.py +314 -0
  410. tokenpak/telemetry/storage_events.py +231 -0
  411. tokenpak/telemetry/storage_rollups.py +340 -0
  412. tokenpak/telemetry/storage_segments.py +88 -0
  413. tokenpak/telemetry/storage_usage.py +361 -0
  414. tokenpak/tests/test_config_validation.py +472 -0
  415. tokenpak/tests/test_config_validation_simple.py +205 -0
  416. tokenpak/tests/test_config_validator_edge_cases.py +350 -0
  417. tokenpak/tests/test_cost_cache_module.py +606 -0
  418. tokenpak/tests/test_cost_integration.py +420 -0
  419. tokenpak/tests/test_proxy_integration.py +88 -0
  420. tokenpak/tests/test_proxy_module.py +359 -0
  421. tokenpak/tests/test_routing_integration.py +23 -0
  422. tokenpak/tests/test_savings_display.py +249 -0
  423. tokenpak/tests/test_schemas_integration.py +79 -0
  424. tokenpak/tests/test_telemetry_integration.py +278 -0
  425. tokenpak/tests/test_validation_gate_integration.py +382 -0
  426. tokenpak/timeline.py +206 -0
  427. tokenpak/tokens.py +134 -0
  428. tokenpak/trace.py +404 -0
  429. tokenpak/user_templates.py +199 -0
  430. tokenpak/validation/__init__.py +63 -0
  431. tokenpak/validation/frontmatter.py +120 -0
  432. tokenpak/validation/request_schema.py +289 -0
  433. tokenpak/validation/request_validator.py +620 -0
  434. tokenpak/validation/response_schema.py +120 -0
  435. tokenpak/validation/validator.py +326 -0
  436. tokenpak/validation_gate.py +145 -0
  437. tokenpak/validator.py +470 -0
  438. tokenpak/vendor_classifier.py +166 -0
  439. tokenpak/version_check.py +147 -0
  440. tokenpak/walker.py +82 -0
  441. tokenpak/watchdog.py +329 -0
  442. tokenpak/wire.py +62 -0
  443. tokenpak/workflow_performance.py +321 -0
  444. tokenpak-1.0.0.dist-info/METADATA +517 -0
  445. tokenpak-1.0.0.dist-info/RECORD +450 -0
  446. tokenpak-1.0.0.dist-info/WHEEL +5 -0
  447. tokenpak-1.0.0.dist-info/entry_points.txt +3 -0
  448. tokenpak-1.0.0.dist-info/licenses/LICENSE +21 -0
  449. tokenpak-1.0.0.dist-info/licenses/LICENSE_COMMERCIAL.md +108 -0
  450. tokenpak-1.0.0.dist-info/top_level.txt +1 -0
tokenpak/__init__.py ADDED
@@ -0,0 +1,171 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """TokenPak — Universal Content Compiler for LLMs.
3
+
4
+ Public API surface for TokenPak v1.0.0.
5
+ Formalizes importable classes for agent integrations, deployment, and testing.
6
+
7
+ Quick start:
8
+ from tokenpak import TelemetryCollector, CacheManager, CompressionEngine, Budgeter
9
+
10
+ Sub-package imports:
11
+ from tokenpak.telemetry import TelemetryCollector
12
+ from tokenpak.engines import CompactionEngine, HeuristicEngine
13
+ from tokenpak.registry import Block, BlockRegistry
14
+ from tokenpak.budgeter import Budgeter
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ __version__ = "1.0.0"
20
+ __author__ = "Kevin Yang"
21
+ __license__ = "MIT"
22
+ __description__ = "Deterministic compression for multi-agent AI workflows"
23
+
24
+ # ---------------------------------------------------------------------------
25
+ # Telemetry
26
+ # ---------------------------------------------------------------------------
27
+ # ---------------------------------------------------------------------------
28
+ # Sub-packages (for advanced use)
29
+ # ---------------------------------------------------------------------------
30
+ from tokenpak import agent, connectors, proxy
31
+
32
+ # CompletionTracker: tracks per-completion cost, model, and latency
33
+ from tokenpak.agent.telemetry.cost_tracker import CostTracker as CompletionTracker
34
+ from tokenpak.budget import BudgetBlock
35
+
36
+ # ---------------------------------------------------------------------------
37
+ # Budgeting
38
+ # ---------------------------------------------------------------------------
39
+ from tokenpak.budgeter import Budgeter
40
+
41
+ # ---------------------------------------------------------------------------
42
+ # CLI
43
+ # ---------------------------------------------------------------------------
44
+ from tokenpak.cli import main
45
+ from tokenpak.engines import get_engine
46
+
47
+ # ---------------------------------------------------------------------------
48
+ # Compression / Compaction Engines
49
+ # ---------------------------------------------------------------------------
50
+ # CompressionEngine: abstract base for all compaction strategies
51
+ from tokenpak.engines.base import CompactionEngine as CompressionEngine
52
+ from tokenpak.engines.heuristic import HeuristicEngine
53
+ from tokenpak.pack import CompiledResult, ContextPack, PackBlock, pack_prompt
54
+
55
+ # ---------------------------------------------------------------------------
56
+ # Content Blocks
57
+ # ---------------------------------------------------------------------------
58
+ from tokenpak.registry import Block, BlockRegistry
59
+
60
+ # ---------------------------------------------------------------------------
61
+ # Compile Reports
62
+ # ---------------------------------------------------------------------------
63
+ from tokenpak.report import Action, CompileReport, Decision
64
+
65
+ # ---------------------------------------------------------------------------
66
+ # Cache
67
+ # ---------------------------------------------------------------------------
68
+ # CacheManager: semantic cache store (get/set/hit-rate tracking)
69
+ from tokenpak.telemetry.cache import CacheStore as CacheManager
70
+ from tokenpak.telemetry.collector import TelemetryCollector
71
+
72
+ # ---------------------------------------------------------------------------
73
+ # Token Counting (Level 1 — single import, zero config)
74
+ # ---------------------------------------------------------------------------
75
+ from tokenpak.tokens import count_tokens
76
+ from tokenpak.trace import ( # noqa: F401
77
+ TokenPakTrace,
78
+ TraceBuilder,
79
+ attach_trace_header,
80
+ attach_trace_envelope,
81
+ strip_trace,
82
+ strip_trace_header,
83
+ read_trace_header,
84
+ read_trace_envelope,
85
+ assert_no_leak,
86
+ )
87
+
88
+
89
+ # ---------------------------------------------------------------------------
90
+ # Agent Handoff Protocol
91
+ # ---------------------------------------------------------------------------
92
+ from tokenpak.agent.agentic.handoff import (
93
+ HandoffBlock,
94
+ HandoffManager,
95
+ HandoffStatus,
96
+ HandoffWire as Handoff,
97
+ ContextRef,
98
+ TokenPak,
99
+ )
100
+ # ---------------------------------------------------------------------------
101
+ # Agentic handoff protocol
102
+ # ---------------------------------------------------------------------------
103
+ from tokenpak.agent.agentic.handoff import (
104
+ ContextRef,
105
+ HandoffBlock,
106
+ HandoffManager,
107
+ HandoffStatus,
108
+ HandoffWire,
109
+ TokenPak,
110
+ )
111
+ # HandoffWire is the intended top-level "Handoff" API (pack-based wire format)
112
+ # The internal Handoff dataclass (file-based) is available via
113
+ # tokenpak.agent.agentic.handoff.Handoff
114
+ Handoff = HandoffWire # type: ignore
115
+
116
+ # ---------------------------------------------------------------------------
117
+ # Public API declaration
118
+ # ---------------------------------------------------------------------------
119
+ __all__ = [
120
+ # Metadata
121
+ "__version__",
122
+ "__author__",
123
+ "__license__",
124
+ "__description__",
125
+ # Telemetry
126
+ "TelemetryCollector",
127
+ "CompletionTracker",
128
+ # Cache
129
+ "CacheManager",
130
+ # Compression
131
+ "CompressionEngine",
132
+ "HeuristicEngine",
133
+ "get_engine",
134
+ # Content Blocks
135
+ "Block",
136
+ "BlockRegistry",
137
+ # Budgeting
138
+ "Budgeter",
139
+ "BudgetBlock",
140
+ # Compile Reports
141
+ "Action",
142
+ "CompileReport",
143
+ "Decision",
144
+ "ContextPack",
145
+ "PackBlock",
146
+ "CompiledResult",
147
+ # Incremental adoption helpers
148
+ "count_tokens",
149
+ "pack_prompt",
150
+ # Agent Handoff Protocol
151
+ "HandoffBlock",
152
+ "HandoffManager",
153
+ "HandoffStatus",
154
+ "Handoff",
155
+ "ContextRef",
156
+ "TokenPak",
157
+ # CLI
158
+ "main",
159
+ # Sub-packages
160
+ "connectors",
161
+ "agent",
162
+ "proxy",
163
+ # Agentic handoff protocol
164
+ "ContextRef",
165
+ "Handoff",
166
+ "HandoffBlock",
167
+ "HandoffManager",
168
+ "HandoffStatus",
169
+ "HandoffWire",
170
+ "TokenPak",
171
+ ]
tokenpak/__main__.py ADDED
@@ -0,0 +1,7 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """Allow `python -m tokenpak` to invoke the CLI."""
3
+
4
+ from tokenpak.cli import main
5
+
6
+ if __name__ == "__main__":
7
+ main()
@@ -0,0 +1,51 @@
1
+ """
2
+ tokenpak.adapters — Unified SDK/framework adapter layer.
3
+
4
+ All adapters share the ``TokenPakAdapter`` base contract:
5
+
6
+ prepare_request(request) → normalised proxy dict
7
+ send(prepared_request) → raw proxy response dict
8
+ parse_response(response) → provider-native response dict
9
+ extract_tokens(response) → token-usage summary dict
10
+
11
+ Quick start
12
+ -----------
13
+ >>> from tokenpak.adapters import AnthropicAdapter, OpenAIAdapter
14
+ >>> adapter = OpenAIAdapter(base_url="http://127.0.0.1:8767", api_key="sk-...")
15
+ >>> response = adapter.call({"model": "gpt-4o", "messages": [...]})
16
+ >>> usage = adapter.extract_tokens(response)
17
+
18
+ See also
19
+ --------
20
+ - ``tokenpak.adapters.base`` — abstract base + exception hierarchy
21
+ - ``tokenpak.adapters.anthropic`` — Anthropic Messages API
22
+ - ``tokenpak.adapters.openai`` — OpenAI Chat Completions API
23
+ - ``tokenpak.adapters.langchain`` — LangChain (ChatOpenAI / ChatAnthropic)
24
+ - ``tokenpak.adapters.litellm`` — LiteLLM provider-agnostic routing
25
+ """
26
+
27
+ from tokenpak.adapters.anthropic import AnthropicAdapter
28
+ from tokenpak.adapters.base import (
29
+ TokenPakAdapter,
30
+ TokenPakAdapterError,
31
+ TokenPakAuthError,
32
+ TokenPakConfigError,
33
+ TokenPakTimeoutError,
34
+ )
35
+ from tokenpak.adapters.langchain import LangChainAdapter
36
+ from tokenpak.adapters.litellm import LiteLLMAdapter
37
+ from tokenpak.adapters.openai import OpenAIAdapter
38
+
39
+ __all__ = [
40
+ # Base
41
+ "TokenPakAdapter",
42
+ "TokenPakAdapterError",
43
+ "TokenPakAuthError",
44
+ "TokenPakConfigError",
45
+ "TokenPakTimeoutError",
46
+ # Concrete adapters
47
+ "AnthropicAdapter",
48
+ "OpenAIAdapter",
49
+ "LangChainAdapter",
50
+ "LiteLLMAdapter",
51
+ ]
@@ -0,0 +1,258 @@
1
+ """
2
+ TokenPak Anthropic SDK Adapter
3
+
4
+ Routes Anthropic ``messages.create`` requests through the TokenPak proxy.
5
+ Preserves the full Anthropic Messages API shape on both request and response
6
+ so callers require zero code changes beyond swapping in this adapter.
7
+
8
+ Request format handled:
9
+ {
10
+ "model": "claude-3-5-sonnet-20241022",
11
+ "max_tokens": 4096,
12
+ "system": "...",
13
+ "messages": [{"role": "user", "content": "..."}],
14
+ ...
15
+ }
16
+
17
+ Token extraction
18
+ ----------------
19
+ Anthropic reports exact usage in every non-streaming response:
20
+ ``usage.input_tokens``, ``usage.output_tokens``,
21
+ ``usage.cache_read_input_tokens``, ``usage.cache_creation_input_tokens``
22
+
23
+ Error handling
24
+ --------------
25
+ - 401/403 → TokenPakAuthError
26
+ - timeout → TokenPakTimeoutError
27
+ - provider error block in response → TokenPakAdapterError
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import json
33
+ import logging
34
+ import time
35
+ from typing import Any
36
+
37
+ try:
38
+ import requests as _requests
39
+ _REQUESTS_AVAILABLE = True
40
+ except ImportError:
41
+ _REQUESTS_AVAILABLE = False
42
+
43
+ from tokenpak.adapters.base import (
44
+ TokenPakAdapter,
45
+ TokenPakAdapterError,
46
+ TokenPakAuthError,
47
+ TokenPakConfigError,
48
+ TokenPakTimeoutError,
49
+ )
50
+
51
+ _log = logging.getLogger("tokenpak.adapters.anthropic")
52
+
53
+ # Required fields in every request
54
+ _REQUIRED_FIELDS = frozenset({"model", "messages", "max_tokens"})
55
+
56
+ # Anthropic stop_reason → canonical finish_reason
57
+ _STOP_REASON_MAP: dict[str, str] = {
58
+ "end_turn": "stop",
59
+ "max_tokens": "max_tokens",
60
+ "stop_sequence": "stop_sequence",
61
+ "tool_use": "tool_use",
62
+ }
63
+
64
+
65
+ class AnthropicAdapter(TokenPakAdapter):
66
+ """TokenPak adapter for the Anthropic Messages API.
67
+
68
+ Usage
69
+ -----
70
+ >>> adapter = AnthropicAdapter(
71
+ ... base_url="http://127.0.0.1:8767",
72
+ ... api_key="sk-ant-...",
73
+ ... )
74
+ >>> response = adapter.call({
75
+ ... "model": "claude-3-5-sonnet-20241022",
76
+ ... "max_tokens": 1024,
77
+ ... "messages": [{"role": "user", "content": "Hello"}],
78
+ ... })
79
+ >>> tokens = adapter.extract_tokens(response)
80
+ """
81
+
82
+ provider_name: str = "anthropic"
83
+
84
+ PROXY_PATH: str = "/v1/messages"
85
+ ANTHROPIC_VERSION: str = "2023-06-01"
86
+
87
+ # ── prepare_request ───────────────────────────────────────────────────
88
+
89
+ def prepare_request(self, request: dict[str, Any]) -> dict[str, Any]:
90
+ """Validate and normalise an Anthropic request.
91
+
92
+ Validation:
93
+ - ``model``, ``messages``, ``max_tokens`` must be present
94
+ - ``messages`` must be a non-empty list
95
+ - Each message must have ``role`` (str) and ``content`` (str | list)
96
+
97
+ Normalisation:
98
+ - Strips unknown top-level keys that would be rejected by the proxy
99
+ (conservative: passes through everything; proxy decides)
100
+ - Adds ``stream: false`` default if not specified
101
+ """
102
+ missing = _REQUIRED_FIELDS - request.keys()
103
+ if missing:
104
+ raise TokenPakConfigError(
105
+ f"AnthropicAdapter.prepare_request: missing required fields: {sorted(missing)}"
106
+ )
107
+
108
+ messages = request.get("messages")
109
+ if not isinstance(messages, list) or not messages:
110
+ raise TokenPakConfigError(
111
+ "AnthropicAdapter.prepare_request: 'messages' must be a non-empty list."
112
+ )
113
+
114
+ for i, msg in enumerate(messages):
115
+ if not isinstance(msg, dict):
116
+ raise TokenPakConfigError(
117
+ f"AnthropicAdapter.prepare_request: messages[{i}] must be a dict."
118
+ )
119
+ if "role" not in msg or "content" not in msg:
120
+ raise TokenPakConfigError(
121
+ f"AnthropicAdapter.prepare_request: messages[{i}] must have 'role' and 'content'."
122
+ )
123
+
124
+ prepared = dict(request)
125
+ prepared.setdefault("stream", False)
126
+
127
+ self.logger.debug(
128
+ "prepare_request model=%s messages=%d",
129
+ prepared.get("model"),
130
+ len(messages),
131
+ )
132
+ return prepared
133
+
134
+ # ── send ──────────────────────────────────────────────────────────────
135
+
136
+ def send(self, prepared_request: dict[str, Any]) -> dict[str, Any]:
137
+ """POST to ``{base_url}/v1/messages`` through the proxy."""
138
+ if not _REQUESTS_AVAILABLE:
139
+ raise TokenPakAdapterError(
140
+ "AnthropicAdapter.send: 'requests' package is required. "
141
+ "Install with: pip install requests"
142
+ )
143
+
144
+ url = f"{self.base_url}{self.PROXY_PATH}"
145
+ headers = {
146
+ "Content-Type": "application/json",
147
+ "x-api-key": self.api_key,
148
+ "anthropic-version": self.ANTHROPIC_VERSION,
149
+ }
150
+
151
+ self.logger.debug("send POST %s model=%s", url, prepared_request.get("model"))
152
+ t0 = time.monotonic()
153
+
154
+ try:
155
+ resp = _requests.post(
156
+ url,
157
+ headers=headers,
158
+ data=json.dumps(prepared_request, ensure_ascii=False).encode("utf-8"),
159
+ timeout=self.timeout_s,
160
+ )
161
+ except _requests.exceptions.Timeout as exc:
162
+ raise TokenPakTimeoutError(
163
+ f"AnthropicAdapter.send: request timed out after {self.timeout_s}s."
164
+ ) from exc
165
+ except _requests.exceptions.RequestException as exc:
166
+ raise TokenPakAdapterError(
167
+ f"AnthropicAdapter.send: HTTP transport error: {exc}"
168
+ ) from exc
169
+
170
+ elapsed_ms = (time.monotonic() - t0) * 1000
171
+ self.logger.info("send complete status=%d elapsed_ms=%.1f", resp.status_code, elapsed_ms)
172
+
173
+ if resp.status_code in (401, 403):
174
+ raise TokenPakAuthError(
175
+ f"AnthropicAdapter.send: authentication failed (HTTP {resp.status_code}).",
176
+ status_code=resp.status_code,
177
+ )
178
+
179
+ if not resp.ok:
180
+ try:
181
+ err_body = resp.json()
182
+ except Exception:
183
+ err_body = resp.text
184
+ raise TokenPakAdapterError(
185
+ f"AnthropicAdapter.send: proxy returned HTTP {resp.status_code}.",
186
+ status_code=resp.status_code,
187
+ raw=err_body,
188
+ )
189
+
190
+ try:
191
+ return resp.json()
192
+ except Exception as exc:
193
+ raise TokenPakAdapterError(
194
+ "AnthropicAdapter.send: response body is not valid JSON."
195
+ ) from exc
196
+
197
+ # ── parse_response ────────────────────────────────────────────────────
198
+
199
+ def parse_response(self, response: dict[str, Any]) -> dict[str, Any]:
200
+ """Validate proxy response and surface provider errors.
201
+
202
+ Returns the response unchanged if valid; raises ``TokenPakAdapterError``
203
+ when the response contains an Anthropic error block.
204
+ """
205
+ error = response.get("error")
206
+ if error:
207
+ if isinstance(error, dict):
208
+ msg = error.get("message", str(error))
209
+ err_type = error.get("type", "unknown")
210
+ else:
211
+ msg, err_type = str(error), "unknown"
212
+ raise TokenPakAdapterError(
213
+ f"AnthropicAdapter.parse_response: provider error [{err_type}]: {msg}",
214
+ raw=response,
215
+ )
216
+
217
+ if "content" not in response and "type" not in response:
218
+ self.logger.warning(
219
+ "parse_response: response missing 'content' and 'type' — "
220
+ "may be malformed: %s",
221
+ list(response.keys()),
222
+ )
223
+
224
+ return response
225
+
226
+ # ── extract_tokens ────────────────────────────────────────────────────
227
+
228
+ def extract_tokens(self, response: dict[str, Any]) -> dict[str, Any]:
229
+ """Extract Anthropic usage block.
230
+
231
+ Returns zeros with a warning if ``usage`` is absent (e.g. streaming
232
+ partial chunks where billing data hasn't arrived yet).
233
+ """
234
+ usage = response.get("usage", {})
235
+ if not usage:
236
+ self.logger.warning(
237
+ "extract_tokens: no 'usage' block in response — returning zeros."
238
+ )
239
+ return {
240
+ "input_tokens": 0,
241
+ "output_tokens": 0,
242
+ "cache_read": 0,
243
+ "cache_write": 0,
244
+ "total": 0,
245
+ }
246
+
247
+ input_tokens = int(usage.get("input_tokens", 0))
248
+ output_tokens = int(usage.get("output_tokens", 0))
249
+ cache_read = int(usage.get("cache_read_input_tokens", 0))
250
+ cache_write = int(usage.get("cache_creation_input_tokens", 0))
251
+
252
+ return {
253
+ "input_tokens": input_tokens,
254
+ "output_tokens": output_tokens,
255
+ "cache_read": cache_read,
256
+ "cache_write": cache_write,
257
+ "total": input_tokens + output_tokens,
258
+ }
@@ -0,0 +1,212 @@
1
+ """
2
+ TokenPak Unified Adapter Base — TokenPakAdapter
3
+
4
+ All SDK/framework adapters (Anthropic, OpenAI, LangChain, LiteLLM, etc.)
5
+ inherit from this class and implement the four lifecycle hooks.
6
+
7
+ Lifecycle
8
+ ---------
9
+ 1. ``prepare_request(request)`` — validate & normalise SDK request → proxy format
10
+ 2. ``send(prepared_request)`` — POST to TokenPak proxy, handle errors/timeouts
11
+ 3. ``parse_response(response)`` — proxy response → SDK-native format
12
+ 4. ``extract_tokens(response)`` — pull token-usage counts for budgeting
13
+
14
+ Error Handling Contract
15
+ -----------------------
16
+ All concrete adapters MUST:
17
+ - Wrap HTTP errors in ``TokenPakAdapterError``
18
+ - Wrap timeout errors in ``TokenPakTimeoutError``
19
+ - Wrap auth/config errors in ``TokenPakConfigError``
20
+ - Never raise bare ``requests.exceptions.*`` or provider SDK exceptions
21
+ directly — always translate to the canonical hierarchy.
22
+
23
+ Logging
24
+ -------
25
+ Use ``self.logger`` (standard ``logging.Logger``) for all log output.
26
+ Log levels:
27
+ - DEBUG : request/response body summaries (no credentials)
28
+ - INFO : round-trip timings, cache-hit notices
29
+ - WARNING : retried requests, degraded fallbacks
30
+ - ERROR : terminal failures before raising exceptions
31
+ """
32
+
33
+ from __future__ import annotations
34
+
35
+ import logging
36
+ import time
37
+ from abc import ABC, abstractmethod
38
+ from typing import Any
39
+
40
+ # ── Canonical exception hierarchy ─────────────────────────────────────────
41
+
42
+ class TokenPakAdapterError(Exception):
43
+ """Base exception for all TokenPak adapter errors."""
44
+
45
+ def __init__(self, message: str, status_code: int | None = None, raw: Any = None) -> None:
46
+ super().__init__(message)
47
+ self.status_code = status_code
48
+ self.raw = raw
49
+
50
+
51
+ class TokenPakTimeoutError(TokenPakAdapterError):
52
+ """Raised when the proxy does not respond within ``timeout_s`` seconds."""
53
+
54
+
55
+ class TokenPakConfigError(TokenPakAdapterError):
56
+ """Raised when required configuration (base_url, api_key) is missing/invalid."""
57
+
58
+
59
+ class TokenPakAuthError(TokenPakAdapterError):
60
+ """Raised on 401/403 responses from the proxy."""
61
+
62
+
63
+ # ── Base adapter ───────────────────────────────────────────────────────────
64
+
65
+ class TokenPakAdapter(ABC):
66
+ """Abstract base class for all TokenPak SDK/framework adapters.
67
+
68
+ Parameters
69
+ ----------
70
+ base_url:
71
+ TokenPak proxy endpoint, e.g. ``"http://127.0.0.1:8767"``.
72
+ Must not have a trailing slash.
73
+ api_key:
74
+ Provider API key forwarded transparently through the proxy.
75
+ timeout_s:
76
+ Request timeout in seconds. Defaults to 120.
77
+ """
78
+
79
+ #: Subclasses set this to a stable, lowercase identifier (e.g. "anthropic").
80
+ provider_name: str = "unknown"
81
+
82
+ DEFAULT_TIMEOUT_S: float = 120.0
83
+
84
+ def __init__(self, base_url: str, api_key: str, timeout_s: float | None = None) -> None:
85
+ if not base_url:
86
+ raise TokenPakConfigError("base_url must not be empty.")
87
+ if not api_key:
88
+ raise TokenPakConfigError("api_key must not be empty.")
89
+
90
+ self.base_url = base_url.rstrip("/")
91
+ self.api_key = api_key
92
+ self.timeout_s = timeout_s if timeout_s is not None else self.DEFAULT_TIMEOUT_S
93
+ self.logger = logging.getLogger(f"tokenpak.adapters.{self.provider_name}")
94
+
95
+ # ── Lifecycle hooks ────────────────────────────────────────────────────
96
+
97
+ @abstractmethod
98
+ def prepare_request(self, request: dict[str, Any]) -> dict[str, Any]:
99
+ """Validate and normalise an SDK request dict into proxy format.
100
+
101
+ Parameters
102
+ ----------
103
+ request:
104
+ Raw dict as the SDK caller would build it (model, messages, etc.).
105
+
106
+ Returns
107
+ -------
108
+ dict
109
+ Proxy-ready request dict. Must contain at least ``"model"``
110
+ and ``"messages"`` (or the provider's equivalent).
111
+
112
+ Raises
113
+ ------
114
+ TokenPakConfigError
115
+ If required fields are missing or have invalid types.
116
+ """
117
+
118
+ @abstractmethod
119
+ def send(self, prepared_request: dict[str, Any]) -> dict[str, Any]:
120
+ """POST *prepared_request* to the TokenPak proxy and return the response.
121
+
122
+ Parameters
123
+ ----------
124
+ prepared_request:
125
+ The dict returned by ``prepare_request``.
126
+
127
+ Returns
128
+ -------
129
+ dict
130
+ Raw response dict from the proxy.
131
+
132
+ Raises
133
+ ------
134
+ TokenPakTimeoutError
135
+ If the request exceeds ``self.timeout_s``.
136
+ TokenPakAuthError
137
+ On 401/403 responses.
138
+ TokenPakAdapterError
139
+ For all other HTTP/transport errors.
140
+ """
141
+
142
+ @abstractmethod
143
+ def parse_response(self, response: dict[str, Any]) -> dict[str, Any]:
144
+ """Convert a raw proxy response into the provider's native SDK format.
145
+
146
+ Parameters
147
+ ----------
148
+ response:
149
+ Raw dict returned by ``send``.
150
+
151
+ Returns
152
+ -------
153
+ dict
154
+ Response shaped exactly as the provider's SDK would return it,
155
+ so callers can switch to TokenPak without code changes.
156
+
157
+ Raises
158
+ ------
159
+ TokenPakAdapterError
160
+ If the response is malformed or contains a provider error.
161
+ """
162
+
163
+ @abstractmethod
164
+ def extract_tokens(self, response: dict[str, Any]) -> dict[str, Any]:
165
+ """Extract token usage counts from a response.
166
+
167
+ Parameters
168
+ ----------
169
+ response:
170
+ Either the raw proxy dict or the parsed SDK-format dict.
171
+
172
+ Returns
173
+ -------
174
+ dict with keys:
175
+ - ``input_tokens`` (int) — billed input tokens
176
+ - ``output_tokens`` (int) — billed output tokens
177
+ - ``cache_read`` (int) — tokens served from cache (0 if N/A)
178
+ - ``cache_write`` (int) — tokens written to cache (0 if N/A)
179
+ - ``total`` (int) — ``input_tokens + output_tokens``
180
+ """
181
+
182
+ # ── Convenience helpers ────────────────────────────────────────────────
183
+
184
+ def call(self, request: dict[str, Any]) -> dict[str, Any]:
185
+ """Full pipeline: prepare → send → parse_response.
186
+
187
+ Returns the provider-native response dict. Logs round-trip time
188
+ at INFO level.
189
+
190
+ Parameters
191
+ ----------
192
+ request:
193
+ Raw SDK-style request dict.
194
+ """
195
+ t0 = time.monotonic()
196
+ prepared = self.prepare_request(request)
197
+ raw_response = self.send(prepared)
198
+ parsed = self.parse_response(raw_response)
199
+ elapsed_ms = (time.monotonic() - t0) * 1000
200
+ self.logger.info(
201
+ "call complete provider=%s model=%s elapsed_ms=%.1f",
202
+ self.provider_name,
203
+ request.get("model", "?"),
204
+ elapsed_ms,
205
+ )
206
+ return parsed
207
+
208
+ def __repr__(self) -> str:
209
+ return (
210
+ f"<{type(self).__name__} provider={self.provider_name!r}"
211
+ f" base_url={self.base_url!r}>"
212
+ )