fervis 0.1.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 (617) hide show
  1. fervis/__init__.py +51 -0
  2. fervis/django/__init__.py +6 -0
  3. fervis/django/urls.py +5 -0
  4. fervis/error_codes.py +24 -0
  5. fervis/errors.py +388 -0
  6. fervis/evaluation/__init__.py +1 -0
  7. fervis/fastapi/__init__.py +11 -0
  8. fervis/flask/__init__.py +11 -0
  9. fervis/host_api/__init__.py +0 -0
  10. fervis/host_api/adapters/__init__.py +0 -0
  11. fervis/host_api/adapters/django/__init__.py +1 -0
  12. fervis/host_api/adapters/django/adapter.py +99 -0
  13. fervis/host_api/adapters/django/catalog.py +913 -0
  14. fervis/host_api/adapters/django/executor.py +94 -0
  15. fervis/host_api/adapters/django/principal.py +36 -0
  16. fervis/host_api/adapters/django/schema_introspection.py +362 -0
  17. fervis/host_api/adapters/fastapi/__init__.py +1 -0
  18. fervis/host_api/adapters/fastapi/adapter.py +147 -0
  19. fervis/host_api/adapters/fastapi/catalog.py +50 -0
  20. fervis/host_api/adapters/fastapi/executor.py +207 -0
  21. fervis/host_api/adapters/fastapi/loading.py +52 -0
  22. fervis/host_api/adapters/flask/__init__.py +5 -0
  23. fervis/host_api/adapters/flask/adapter.py +109 -0
  24. fervis/host_api/adapters/flask/apispec.py +116 -0
  25. fervis/host_api/adapters/flask/catalog.py +339 -0
  26. fervis/host_api/adapters/flask/executor.py +113 -0
  27. fervis/host_api/adapters/flask/flask_appbuilder.py +91 -0
  28. fervis/host_api/adapters/flask/loading.py +69 -0
  29. fervis/host_api/adapters/flask/principal.py +140 -0
  30. fervis/host_api/adapters/flask/transport.py +92 -0
  31. fervis/host_api/adapters/get_execution.py +163 -0
  32. fervis/host_api/adapters/http.py +242 -0
  33. fervis/host_api/adapters/jsonapi_schema.py +89 -0
  34. fervis/host_api/adapters/marshmallow_schema.py +86 -0
  35. fervis/host_api/adapters/openapi/__init__.py +5 -0
  36. fervis/host_api/adapters/openapi/document.py +266 -0
  37. fervis/host_api/adapters/openapi/model.py +26 -0
  38. fervis/host_api/adapters/openapi/translate.py +242 -0
  39. fervis/host_api/adapters/response_body.py +61 -0
  40. fervis/host_api/adapters/runtime_output.py +14 -0
  41. fervis/host_api/compilation/__init__.py +5 -0
  42. fervis/host_api/compilation/read_request.py +161 -0
  43. fervis/host_api/context.py +78 -0
  44. fervis/host_api/contracts/__init__.py +36 -0
  45. fervis/host_api/contracts/authority.py +133 -0
  46. fervis/host_api/contracts/capabilities.py +141 -0
  47. fervis/host_api/contracts/credentials.py +54 -0
  48. fervis/host_api/contracts/endpoint.py +243 -0
  49. fervis/host_api/contracts/execution.py +23 -0
  50. fervis/host_api/contracts/ports.py +56 -0
  51. fervis/host_api/contracts/read.py +37 -0
  52. fervis/host_api/contracts/response_envelope.py +24 -0
  53. fervis/host_api/credentials.py +244 -0
  54. fervis/interfaces/agent/__init__.py +0 -0
  55. fervis/interfaces/agent/actions.py +275 -0
  56. fervis/interfaces/agent/commands.py +202 -0
  57. fervis/interfaces/cli/__init__.py +6 -0
  58. fervis/interfaces/cli/commands/__init__.py +1 -0
  59. fervis/interfaces/cli/commands/auth.py +60 -0
  60. fervis/interfaces/cli/commands/catalog.py +33 -0
  61. fervis/interfaces/cli/commands/common.py +63 -0
  62. fervis/interfaces/cli/commands/config.py +66 -0
  63. fervis/interfaces/cli/commands/doctor.py +58 -0
  64. fervis/interfaces/cli/commands/explain.py +173 -0
  65. fervis/interfaces/cli/commands/goldset.py +57 -0
  66. fervis/interfaces/cli/commands/init.py +51 -0
  67. fervis/interfaces/cli/commands/inspect.py +87 -0
  68. fervis/interfaces/cli/commands/migrate.py +59 -0
  69. fervis/interfaces/cli/commands/models.py +47 -0
  70. fervis/interfaces/cli/commands/project.py +56 -0
  71. fervis/interfaces/cli/commands/runtime.py +44 -0
  72. fervis/interfaces/cli/commands/scopes.py +79 -0
  73. fervis/interfaces/cli/commands/sources.py +57 -0
  74. fervis/interfaces/cli/commands/usage.py +78 -0
  75. fervis/interfaces/cli/commands/worker.py +36 -0
  76. fervis/interfaces/cli/contracts.py +112 -0
  77. fervis/interfaces/cli/dispatch.py +388 -0
  78. fervis/interfaces/cli/django.py +50 -0
  79. fervis/interfaces/cli/envelope.py +63 -0
  80. fervis/interfaces/cli/main.py +155 -0
  81. fervis/interfaces/cli/parsers.py +479 -0
  82. fervis/interfaces/cli/principals.py +33 -0
  83. fervis/interfaces/cli/rendering.py +206 -0
  84. fervis/interfaces/cli/runtime_ask.py +381 -0
  85. fervis/interfaces/common/__init__.py +1 -0
  86. fervis/interfaces/common/admission.py +84 -0
  87. fervis/interfaces/common/events.py +89 -0
  88. fervis/interfaces/common/questions.py +561 -0
  89. fervis/interfaces/common/read_contexts.py +45 -0
  90. fervis/interfaces/django/__init__.py +1 -0
  91. fervis/interfaces/django/apps.py +32 -0
  92. fervis/interfaces/django/composition.py +115 -0
  93. fervis/interfaces/django/management/__init__.py +1 -0
  94. fervis/interfaces/django/management/commands/__init__.py +1 -0
  95. fervis/interfaces/django/management/commands/process_fervis_runs.py +95 -0
  96. fervis/interfaces/django/models.py +5 -0
  97. fervis/interfaces/django/principal.py +26 -0
  98. fervis/interfaces/django/question_interface.py +19 -0
  99. fervis/interfaces/django/question_run_ports.py +666 -0
  100. fervis/interfaces/django/run_views.py +302 -0
  101. fervis/interfaces/django/runs.py +61 -0
  102. fervis/interfaces/django/security.py +38 -0
  103. fervis/interfaces/django/throttles.py +11 -0
  104. fervis/interfaces/django/urls.py +43 -0
  105. fervis/interfaces/django/views.py +189 -0
  106. fervis/interfaces/django/worker.py +81 -0
  107. fervis/interfaces/fastapi/__init__.py +5 -0
  108. fervis/interfaces/fastapi/principal.py +72 -0
  109. fervis/interfaces/fastapi/router.py +190 -0
  110. fervis/interfaces/flask/__init__.py +5 -0
  111. fervis/interfaces/flask/blueprint.py +179 -0
  112. fervis/interfaces/flask/principal.py +55 -0
  113. fervis/lineage/__init__.py +1 -0
  114. fervis/lineage/apps.py +7 -0
  115. fervis/lineage/django/__init__.py +1 -0
  116. fervis/lineage/django/recorder.py +11 -0
  117. fervis/lineage/django/runtime_failures.py +52 -0
  118. fervis/lineage/django/runtime_spine.py +67 -0
  119. fervis/lineage/django/store.py +125 -0
  120. fervis/lineage/django/terminal_results.py +34 -0
  121. fervis/lineage/enums.py +200 -0
  122. fervis/lineage/ids.py +16 -0
  123. fervis/lineage/memory_artifacts.py +22 -0
  124. fervis/lineage/migrations/0001_initial.py +893 -0
  125. fervis/lineage/migrations/0002_answer_answerpresentation_clarificationrequest_and_more.py +576 -0
  126. fervis/lineage/migrations/0003_remove_clarificationrequest_fervis_clar_req_scope_ck_and_more.py +35 -0
  127. fervis/lineage/migrations/0004_alter_runtimeerrordetail_error_kind.py +35 -0
  128. fervis/lineage/migrations/0005_modelcall_tool_spec_chars.py +17 -0
  129. fervis/lineage/migrations/0006_memoryartifact.py +97 -0
  130. fervis/lineage/migrations/0007_modelcall_model_subcalls_json.py +17 -0
  131. fervis/lineage/migrations/0008_alter_runtimeerrordetail_error_kind.py +34 -0
  132. fervis/lineage/migrations/0009_catalog_endpoint_source_read_fk.py +118 -0
  133. fervis/lineage/migrations/0010_run_scoped_catalog_endpoints.py +11 -0
  134. fervis/lineage/migrations/0011_catalog_endpoint_source_read_same_run.py +37 -0
  135. fervis/lineage/migrations/0012_conversation_read_context_ref.py +20 -0
  136. fervis/lineage/migrations/__init__.py +1 -0
  137. fervis/lineage/model_calls.py +312 -0
  138. fervis/lineage/models.py +928 -0
  139. fervis/lineage/payloads/__init__.py +1 -0
  140. fervis/lineage/payloads/execution_proof_graph.py +190 -0
  141. fervis/lineage/ports.py +128 -0
  142. fervis/lineage/proof_projection.py +68 -0
  143. fervis/lineage/proof_summary.py +174 -0
  144. fervis/lineage/recorder.py +655 -0
  145. fervis/lineage/recorder_core.py +671 -0
  146. fervis/lineage/records.py +786 -0
  147. fervis/lineage/run_chain.py +61 -0
  148. fervis/lineage/run_spine.py +140 -0
  149. fervis/lineage/step_summary.py +140 -0
  150. fervis/lineage/views/__init__.py +1 -0
  151. fervis/lineage/views/agent.py +591 -0
  152. fervis/lineage/views/detail.py +29 -0
  153. fervis/lineage/views/django.py +466 -0
  154. fervis/lineage/views/explain.py +446 -0
  155. fervis/lineage/views/explanation.py +36 -0
  156. fervis/lineage/views/input_lineage.py +179 -0
  157. fervis/lineage/views/json_payload.py +24 -0
  158. fervis/lineage/views/model.py +435 -0
  159. fervis/lineage/views/query.py +239 -0
  160. fervis/lineage/views/render.py +490 -0
  161. fervis/lineage/views/service.py +819 -0
  162. fervis/lineage/views/step_presenters.py +186 -0
  163. fervis/lineage/views/timeline.py +208 -0
  164. fervis/lookup/__init__.py +1 -0
  165. fervis/lookup/answer_rendering/__init__.py +15 -0
  166. fervis/lookup/answer_rendering/model.py +21 -0
  167. fervis/lookup/answer_rendering/renderer.py +198 -0
  168. fervis/lookup/conversation_resolution/__init__.py +65 -0
  169. fervis/lookup/conversation_resolution/clarifications.py +30 -0
  170. fervis/lookup/conversation_resolution/model.py +381 -0
  171. fervis/lookup/conversation_resolution/overlay.py +445 -0
  172. fervis/lookup/conversation_resolution/parser.py +852 -0
  173. fervis/lookup/conversation_resolution/prompt.py +334 -0
  174. fervis/lookup/conversation_resolution/schema.py +289 -0
  175. fervis/lookup/conversation_resolution/tools.py +4 -0
  176. fervis/lookup/conversation_resolution/turn.py +167 -0
  177. fervis/lookup/errors.py +26 -0
  178. fervis/lookup/fact_plan/__init__.py +1 -0
  179. fervis/lookup/fact_plan/fact_plan.py +125 -0
  180. fervis/lookup/fact_plan/field_types.py +17 -0
  181. fervis/lookup/fact_plan/operations.py +235 -0
  182. fervis/lookup/fact_plan/relations.py +155 -0
  183. fervis/lookup/fact_plan/render_spec.py +28 -0
  184. fervis/lookup/fact_plan/row_sources/__init__.py +82 -0
  185. fervis/lookup/fact_plan/row_sources/builder.py +614 -0
  186. fervis/lookup/fact_plan/row_sources/evidence.py +69 -0
  187. fervis/lookup/fact_plan/row_sources/field_paths.py +208 -0
  188. fervis/lookup/fact_plan/row_sources/lookup.py +31 -0
  189. fervis/lookup/fact_plan/row_sources/model.py +117 -0
  190. fervis/lookup/fact_plan/row_sources/payload.py +194 -0
  191. fervis/lookup/fact_plan/row_sources/source_groups.py +51 -0
  192. fervis/lookup/fact_plan/values.py +285 -0
  193. fervis/lookup/fact_planning/__init__.py +1 -0
  194. fervis/lookup/fact_planning/aggregate_choice_parts.py +80 -0
  195. fervis/lookup/fact_planning/available_relations.py +581 -0
  196. fervis/lookup/fact_planning/blocked_evidence.py +144 -0
  197. fervis/lookup/fact_planning/executable_support.py +234 -0
  198. fervis/lookup/fact_planning/fact_planning_family_schema.py +82 -0
  199. fervis/lookup/fact_planning/fact_requirements.py +188 -0
  200. fervis/lookup/fact_planning/fulfillment_evidence.py +151 -0
  201. fervis/lookup/fact_planning/grounded_params.py +89 -0
  202. fervis/lookup/fact_planning/grouped_ranked_choices.py +641 -0
  203. fervis/lookup/fact_planning/lineage_summary.py +60 -0
  204. fervis/lookup/fact_planning/metric_options.py +460 -0
  205. fervis/lookup/fact_planning/parser.py +234 -0
  206. fervis/lookup/fact_planning/pattern_plan/__init__.py +5 -0
  207. fervis/lookup/fact_planning/pattern_plan/aggregate_operations.py +124 -0
  208. fervis/lookup/fact_planning/pattern_plan/aggregate_outputs.py +247 -0
  209. fervis/lookup/fact_planning/pattern_plan/aggregate_patterns.py +531 -0
  210. fervis/lookup/fact_planning/pattern_plan/compiler.py +183 -0
  211. fervis/lookup/fact_planning/pattern_plan/relation_patterns.py +329 -0
  212. fervis/lookup/fact_planning/pattern_plan/render_ids.py +25 -0
  213. fervis/lookup/fact_planning/pattern_plan/row_patterns.py +246 -0
  214. fervis/lookup/fact_planning/pattern_plan/scalar_patterns.py +86 -0
  215. fervis/lookup/fact_planning/pattern_plan/shared.py +762 -0
  216. fervis/lookup/fact_planning/pattern_plan/value_inputs.py +29 -0
  217. fervis/lookup/fact_planning/plan_shapes.py +27 -0
  218. fervis/lookup/fact_planning/prompt_sections.py +233 -0
  219. fervis/lookup/fact_planning/request.py +639 -0
  220. fervis/lookup/fact_planning/required_inputs.py +75 -0
  221. fervis/lookup/fact_planning/row_set_filters.py +277 -0
  222. fervis/lookup/fact_planning/schema.py +319 -0
  223. fervis/lookup/fact_planning/schema_helpers.py +44 -0
  224. fervis/lookup/fact_planning/source_binding_basis.py +35 -0
  225. fervis/lookup/fact_planning/turn.py +241 -0
  226. fervis/lookup/fact_planning/value_components.py +71 -0
  227. fervis/lookup/fact_planning/value_validation.py +72 -0
  228. fervis/lookup/grounding/__init__.py +1 -0
  229. fervis/lookup/grounding/model.py +216 -0
  230. fervis/lookup/grounding/parser.py +140 -0
  231. fervis/lookup/grounding/prompt.py +337 -0
  232. fervis/lookup/grounding/resolution/__init__.py +6 -0
  233. fervis/lookup/grounding/resolution/deterministic.py +310 -0
  234. fervis/lookup/grounding/resolution/model.py +19 -0
  235. fervis/lookup/grounding/resolution/pipeline.py +208 -0
  236. fervis/lookup/grounding/resolution/references.py +1260 -0
  237. fervis/lookup/grounding/resolution/values.py +33 -0
  238. fervis/lookup/grounding/schema.py +205 -0
  239. fervis/lookup/grounding/time_intents.py +274 -0
  240. fervis/lookup/grounding/time_resolution/__init__.py +17 -0
  241. fervis/lookup/grounding/time_resolution/contract.py +78 -0
  242. fervis/lookup/grounding/time_resolution/date_values.py +72 -0
  243. fervis/lookup/grounding/time_resolution/expression_parser.py +270 -0
  244. fervis/lookup/grounding/time_resolution/intent_validation.py +148 -0
  245. fervis/lookup/grounding/time_resolution/resolver.py +593 -0
  246. fervis/lookup/grounding/turn.py +151 -0
  247. fervis/lookup/lineage/__init__.py +1 -0
  248. fervis/lookup/lineage/errors.py +5 -0
  249. fervis/lookup/lineage/explanation_metadata.py +127 -0
  250. fervis/lookup/lineage/model_turns.py +45 -0
  251. fervis/lookup/lineage/results.py +926 -0
  252. fervis/lookup/lineage/source_reads.py +228 -0
  253. fervis/lookup/lineage/step_summaries.py +515 -0
  254. fervis/lookup/lineage/steps.py +448 -0
  255. fervis/lookup/memory/__init__.py +1 -0
  256. fervis/lookup/memory/available_values.py +181 -0
  257. fervis/lookup/memory/lineage_artifacts.py +454 -0
  258. fervis/lookup/memory/outcomes.py +396 -0
  259. fervis/lookup/memory/projection.py +1651 -0
  260. fervis/lookup/operation_families/__init__.py +1 -0
  261. fervis/lookup/operation_families/common/__init__.py +1 -0
  262. fervis/lookup/operation_families/common/source_binding_metric_fit.py +412 -0
  263. fervis/lookup/operation_families/comparison_check/__init__.py +1 -0
  264. fervis/lookup/operation_families/comparison_check/plan_selection.py +16 -0
  265. fervis/lookup/operation_families/computed_scalar/__init__.py +1 -0
  266. fervis/lookup/operation_families/computed_scalar/fact_planning.py +72 -0
  267. fervis/lookup/operation_families/computed_scalar/plan_selection.py +16 -0
  268. fervis/lookup/operation_families/coverage_check/__init__.py +1 -0
  269. fervis/lookup/operation_families/coverage_check/plan_selection.py +16 -0
  270. fervis/lookup/operation_families/existence_check/__init__.py +1 -0
  271. fervis/lookup/operation_families/existence_check/plan_selection.py +16 -0
  272. fervis/lookup/operation_families/fact_planning_schema_registry.py +406 -0
  273. fervis/lookup/operation_families/grouped_aggregate/__init__.py +1 -0
  274. fervis/lookup/operation_families/grouped_aggregate/plan_selection.py +20 -0
  275. fervis/lookup/operation_families/grouped_ranked/__init__.py +1 -0
  276. fervis/lookup/operation_families/grouped_ranked/canonical_groups.py +65 -0
  277. fervis/lookup/operation_families/grouped_ranked/fact_planning.py +156 -0
  278. fervis/lookup/operation_families/grouped_ranked/plan_selection.py +336 -0
  279. fervis/lookup/operation_families/joined_rows/__init__.py +1 -0
  280. fervis/lookup/operation_families/joined_rows/fact_planning.py +229 -0
  281. fervis/lookup/operation_families/list_rows/__init__.py +1 -0
  282. fervis/lookup/operation_families/list_rows/fact_planning.py +53 -0
  283. fervis/lookup/operation_families/list_rows/plan_selection.py +28 -0
  284. fervis/lookup/operation_families/plan_selection_registry.py +83 -0
  285. fervis/lookup/operation_families/ranked_selection/__init__.py +1 -0
  286. fervis/lookup/operation_families/ranked_selection/plan_selection.py +20 -0
  287. fervis/lookup/operation_families/scalar_aggregate/__init__.py +1 -0
  288. fervis/lookup/operation_families/scalar_aggregate/fact_planning.py +147 -0
  289. fervis/lookup/operation_families/scalar_aggregate/plan_selection.py +16 -0
  290. fervis/lookup/operation_families/scalar_value/__init__.py +1 -0
  291. fervis/lookup/operation_families/scalar_value/fact_planning.py +29 -0
  292. fervis/lookup/operation_families/scalar_value/plan_selection.py +16 -0
  293. fervis/lookup/operation_families/set_difference/__init__.py +1 -0
  294. fervis/lookup/operation_families/set_difference/fact_planning.py +204 -0
  295. fervis/lookup/operation_families/set_difference/plan_selection.py +16 -0
  296. fervis/lookup/operation_families/source_binding_registry.py +13 -0
  297. fervis/lookup/orchestration/__init__.py +1 -0
  298. fervis/lookup/orchestration/limits.py +163 -0
  299. fervis/lookup/orchestration/model_turn_events.py +88 -0
  300. fervis/lookup/orchestration/pipeline.py +1972 -0
  301. fervis/lookup/orchestration/question_lookup_port.py +86 -0
  302. fervis/lookup/orchestration/request.py +49 -0
  303. fervis/lookup/orchestration/result.py +67 -0
  304. fervis/lookup/orchestration/result_synthesis.py +164 -0
  305. fervis/lookup/orchestration/service.py +274 -0
  306. fervis/lookup/orchestration/terminal_results.py +240 -0
  307. fervis/lookup/outcomes/__init__.py +1 -0
  308. fervis/lookup/outcomes/answerability.py +100 -0
  309. fervis/lookup/outcomes/clarifications.py +54 -0
  310. fervis/lookup/outcomes/classification.py +299 -0
  311. fervis/lookup/outcomes/errors.py +72 -0
  312. fervis/lookup/outcomes/model.py +133 -0
  313. fervis/lookup/outcomes/operation_semantics.py +86 -0
  314. fervis/lookup/outcomes/terminal_details.py +134 -0
  315. fervis/lookup/plan_execution/__init__.py +1 -0
  316. fervis/lookup/plan_execution/authorized_sources.py +96 -0
  317. fervis/lookup/plan_execution/compiled_execution.py +636 -0
  318. fervis/lookup/plan_execution/errors.py +9 -0
  319. fervis/lookup/plan_execution/generated_relations.py +66 -0
  320. fervis/lookup/plan_execution/operation_engine/__init__.py +5 -0
  321. fervis/lookup/plan_execution/operation_engine/aggregate_operations.py +133 -0
  322. fervis/lookup/plan_execution/operation_engine/compute_operations.py +58 -0
  323. fervis/lookup/plan_execution/operation_engine/engine.py +193 -0
  324. fervis/lookup/plan_execution/operation_engine/predicates.py +69 -0
  325. fervis/lookup/plan_execution/operation_engine/relation_operations.py +357 -0
  326. fervis/lookup/plan_execution/operation_engine/shared.py +573 -0
  327. fervis/lookup/plan_execution/operation_runtime.py +42 -0
  328. fervis/lookup/plan_execution/relations.py +175 -0
  329. fervis/lookup/plan_execution/runner.py +857 -0
  330. fervis/lookup/plan_execution/value_compiler.py +548 -0
  331. fervis/lookup/plan_execution/verification/__init__.py +80 -0
  332. fervis/lookup/plan_execution/verification/_shared.py +89 -0
  333. fervis/lookup/plan_execution/verification/answer_plan.py +212 -0
  334. fervis/lookup/plan_execution/verification/blocked.py +57 -0
  335. fervis/lookup/plan_execution/verification/contract_types.py +140 -0
  336. fervis/lookup/plan_execution/verification/contracts.py +148 -0
  337. fervis/lookup/plan_execution/verification/execution_proof.py +30 -0
  338. fervis/lookup/plan_execution/verification/normalization.py +150 -0
  339. fervis/lookup/plan_execution/verification/operation_contracts.py +488 -0
  340. fervis/lookup/plan_execution/verification/operation_invariants.py +299 -0
  341. fervis/lookup/plan_execution/verification/operations.py +338 -0
  342. fervis/lookup/plan_execution/verification/question_contract.py +75 -0
  343. fervis/lookup/plan_execution/verification/render.py +162 -0
  344. fervis/lookup/plan_execution/verification/scalars.py +19 -0
  345. fervis/lookup/plan_execution/verification/sources.py +399 -0
  346. fervis/lookup/plan_execution/verification/terminals.py +312 -0
  347. fervis/lookup/plan_selection/__init__.py +41 -0
  348. fervis/lookup/plan_selection/family_specs.py +187 -0
  349. fervis/lookup/plan_selection/model.py +296 -0
  350. fervis/lookup/plan_selection/parser.py +237 -0
  351. fervis/lookup/plan_selection/prompt.py +180 -0
  352. fervis/lookup/plan_selection/role_semantics.py +82 -0
  353. fervis/lookup/plan_selection/schema.py +87 -0
  354. fervis/lookup/plan_selection/source_strategies.py +652 -0
  355. fervis/lookup/plan_selection/support_options.py +192 -0
  356. fervis/lookup/plan_selection/turn.py +144 -0
  357. fervis/lookup/query_enrichment/__init__.py +25 -0
  358. fervis/lookup/query_enrichment/model.py +60 -0
  359. fervis/lookup/query_enrichment/parser.py +266 -0
  360. fervis/lookup/query_enrichment/prompt.py +225 -0
  361. fervis/lookup/query_enrichment/schema.py +100 -0
  362. fervis/lookup/query_enrichment/turn.py +155 -0
  363. fervis/lookup/question_contract/__init__.py +101 -0
  364. fervis/lookup/question_contract/_normalization.py +7 -0
  365. fervis/lookup/question_contract/answer_output_support.py +26 -0
  366. fervis/lookup/question_contract/model.py +940 -0
  367. fervis/lookup/question_contract/parser.py +947 -0
  368. fervis/lookup/question_contract/prompt.py +217 -0
  369. fervis/lookup/question_contract/schema.py +362 -0
  370. fervis/lookup/question_contract/tools.py +9 -0
  371. fervis/lookup/question_contract/turn.py +196 -0
  372. fervis/lookup/read_eligibility/__init__.py +53 -0
  373. fervis/lookup/read_eligibility/candidate_identity.py +71 -0
  374. fervis/lookup/read_eligibility/candidate_scope.py +127 -0
  375. fervis/lookup/read_eligibility/cards.py +220 -0
  376. fervis/lookup/read_eligibility/filtering.py +64 -0
  377. fervis/lookup/read_eligibility/model.py +82 -0
  378. fervis/lookup/read_eligibility/parser.py +349 -0
  379. fervis/lookup/read_eligibility/prompt.py +182 -0
  380. fervis/lookup/read_eligibility/recall.py +121 -0
  381. fervis/lookup/read_eligibility/row_paths.py +56 -0
  382. fervis/lookup/read_eligibility/schema.py +170 -0
  383. fervis/lookup/read_eligibility/source_groups.py +34 -0
  384. fervis/lookup/read_eligibility/support_projection.py +27 -0
  385. fervis/lookup/read_eligibility/surface.py +35 -0
  386. fervis/lookup/read_eligibility/turn.py +159 -0
  387. fervis/lookup/relation_catalog/__init__.py +53 -0
  388. fervis/lookup/relation_catalog/facts.py +56 -0
  389. fervis/lookup/relation_catalog/from_host_api.py +347 -0
  390. fervis/lookup/relation_catalog/identity.py +68 -0
  391. fervis/lookup/relation_catalog/interface_tokens.py +70 -0
  392. fervis/lookup/relation_catalog/model.py +165 -0
  393. fervis/lookup/relation_catalog/ports.py +17 -0
  394. fervis/lookup/relation_catalog/row_paths.py +24 -0
  395. fervis/lookup/relation_catalog/selection/__init__.py +47 -0
  396. fervis/lookup/relation_catalog/selection/model.py +98 -0
  397. fervis/lookup/relation_catalog/selection/results.py +47 -0
  398. fervis/lookup/relation_catalog/selection/selector/__init__.py +12 -0
  399. fervis/lookup/relation_catalog/selection/selector/constants.py +224 -0
  400. fervis/lookup/relation_catalog/selection/selector/fact_selection.py +654 -0
  401. fervis/lookup/relation_catalog/selection/selector/resolver.py +205 -0
  402. fervis/lookup/relation_catalog/selection/selector/terms.py +134 -0
  403. fervis/lookup/relation_catalog/validation.py +172 -0
  404. fervis/lookup/source_binding/__init__.py +57 -0
  405. fervis/lookup/source_binding/candidates/__init__.py +33 -0
  406. fervis/lookup/source_binding/candidates/_shared.py +46 -0
  407. fervis/lookup/source_binding/candidates/api_sources.py +140 -0
  408. fervis/lookup/source_binding/candidates/bindings.py +22 -0
  409. fervis/lookup/source_binding/candidates/bound_payload.py +228 -0
  410. fervis/lookup/source_binding/candidates/candidate_tree.py +93 -0
  411. fervis/lookup/source_binding/candidates/compact.py +336 -0
  412. fervis/lookup/source_binding/candidates/eligibility.py +164 -0
  413. fervis/lookup/source_binding/candidates/evidence.py +211 -0
  414. fervis/lookup/source_binding/candidates/fulfillment_slots.py +403 -0
  415. fervis/lookup/source_binding/candidates/handles.py +211 -0
  416. fervis/lookup/source_binding/candidates/indexes.py +120 -0
  417. fervis/lookup/source_binding/candidates/memory.py +89 -0
  418. fervis/lookup/source_binding/candidates/model.py +26 -0
  419. fervis/lookup/source_binding/candidates/model_visible_evidence.py +104 -0
  420. fervis/lookup/source_binding/candidates/params.py +511 -0
  421. fervis/lookup/source_binding/candidates/plan_selection_filter.py +140 -0
  422. fervis/lookup/source_binding/candidates/population.py +48 -0
  423. fervis/lookup/source_binding/candidates/population_roles.py +148 -0
  424. fervis/lookup/source_binding/candidates/raw_payload.py +533 -0
  425. fervis/lookup/source_binding/candidates/registry.py +273 -0
  426. fervis/lookup/source_binding/candidates/registry_builder.py +346 -0
  427. fervis/lookup/source_binding/candidates/row_population.py +26 -0
  428. fervis/lookup/source_binding/candidates/row_predicates.py +70 -0
  429. fervis/lookup/source_binding/candidates/same_scope.py +287 -0
  430. fervis/lookup/source_binding/candidates/scope_values.py +47 -0
  431. fervis/lookup/source_binding/candidates/values.py +87 -0
  432. fervis/lookup/source_binding/evidence_types.py +15 -0
  433. fervis/lookup/source_binding/lineage_summary.py +203 -0
  434. fervis/lookup/source_binding/membership_tests.py +32 -0
  435. fervis/lookup/source_binding/memory_context.py +132 -0
  436. fervis/lookup/source_binding/metric_fit.py +7 -0
  437. fervis/lookup/source_binding/model.py +214 -0
  438. fervis/lookup/source_binding/normal_instance_roles.py +126 -0
  439. fervis/lookup/source_binding/param_surface.py +63 -0
  440. fervis/lookup/source_binding/param_values.py +13 -0
  441. fervis/lookup/source_binding/parser.py +2289 -0
  442. fervis/lookup/source_binding/parser_common.py +51 -0
  443. fervis/lookup/source_binding/population_bindings.py +304 -0
  444. fervis/lookup/source_binding/prompt.py +503 -0
  445. fervis/lookup/source_binding/schema.py +972 -0
  446. fervis/lookup/source_binding/terminal_outcomes.py +164 -0
  447. fervis/lookup/source_binding/terminal_parser.py +129 -0
  448. fervis/lookup/source_binding/turn.py +307 -0
  449. fervis/lookup/source_reads/__init__.py +1 -0
  450. fervis/lookup/source_reads/response.py +231 -0
  451. fervis/lookup/turn_prompts/__init__.py +55 -0
  452. fervis/lookup/turn_prompts/approval.py +30 -0
  453. fervis/lookup/turn_prompts/builder.py +158 -0
  454. fervis/lookup/turn_prompts/context.py +136 -0
  455. fervis/lookup/turn_prompts/invocation.py +58 -0
  456. fervis/lookup/turn_prompts/projections/__init__.py +17 -0
  457. fervis/lookup/turn_prompts/projections/response_shape.py +1047 -0
  458. fervis/lookup/turn_prompts/rendering.py +14 -0
  459. fervis/lookup/turn_prompts/sections.py +38 -0
  460. fervis/lookup/turn_prompts/turn_prompt.py +69 -0
  461. fervis/memory/__init__.py +1 -0
  462. fervis/memory/_serialization.py +13 -0
  463. fervis/memory/activation.py +182 -0
  464. fervis/memory/addresses.py +397 -0
  465. fervis/memory/answer_outputs.py +214 -0
  466. fervis/memory/artifacts.py +79 -0
  467. fervis/memory/conversation_context/__init__.py +23 -0
  468. fervis/memory/conversation_context/activation.py +355 -0
  469. fervis/memory/conversation_context/model.py +157 -0
  470. fervis/memory/identities.py +309 -0
  471. fervis/memory/lineage.py +123 -0
  472. fervis/memory/projection.py +210 -0
  473. fervis/memory/prompt_contract.py +35 -0
  474. fervis/model_io/__init__.py +1 -0
  475. fervis/model_io/backbone/__init__.py +1 -0
  476. fervis/model_io/backbone/contracts.py +71 -0
  477. fervis/model_io/backbone/dto.py +69 -0
  478. fervis/model_io/backbone/factory.py +94 -0
  479. fervis/model_io/backbone/model_routing.py +60 -0
  480. fervis/model_io/backbone/registry.py +67 -0
  481. fervis/model_io/backbone/tool_codec.py +65 -0
  482. fervis/model_io/models.py +23 -0
  483. fervis/model_io/pricing/__init__.py +5 -0
  484. fervis/model_io/pricing/models_dev.py +100 -0
  485. fervis/model_io/providers/__init__.py +1 -0
  486. fervis/model_io/providers/anthropic_adapter/__init__.py +57 -0
  487. fervis/model_io/providers/anthropic_adapter/conversation_resolution_transport.py +26 -0
  488. fervis/model_io/providers/anthropic_adapter/loop_adapter.py +276 -0
  489. fervis/model_io/providers/bootstrap.py +28 -0
  490. fervis/model_io/providers/chat_runtime.py +674 -0
  491. fervis/model_io/providers/openai_compatible_adapter/__init__.py +78 -0
  492. fervis/model_io/providers/openai_compatible_adapter/loop_adapter.py +353 -0
  493. fervis/model_io/providers/schema_projection.py +24 -0
  494. fervis/model_io/providers/session_runtime.py +48 -0
  495. fervis/model_io/providers/shared_runtime.py +97 -0
  496. fervis/model_io/providers/specs.py +97 -0
  497. fervis/model_io/routing/__init__.py +13 -0
  498. fervis/model_io/routing/router.py +165 -0
  499. fervis/model_io/structured_output/__init__.py +1 -0
  500. fervis/model_io/structured_output/errors.py +88 -0
  501. fervis/model_io/structured_output/generation.py +99 -0
  502. fervis/model_io/structured_output/parsing.py +19 -0
  503. fervis/model_io/structured_output/provider_budget.py +22 -0
  504. fervis/model_io/structured_output/specs.py +27 -0
  505. fervis/model_io/structured_output/validation.py +111 -0
  506. fervis/model_io/telemetry.py +76 -0
  507. fervis/model_io/turn_artifacts.py +53 -0
  508. fervis/model_io/turns.py +16 -0
  509. fervis/observability/__init__.py +1 -0
  510. fervis/observability/django.py +184 -0
  511. fervis/observability/django_prompt_captures.py +77 -0
  512. fervis/observability/event_contracts.py +77 -0
  513. fervis/observability/model_calls.py +22 -0
  514. fervis/observability/prompt_captures.py +64 -0
  515. fervis/observability/prompt_viewer/__init__.py +1 -0
  516. fervis/observability/prompt_viewer/render_prompts.py +966 -0
  517. fervis/observability/query.py +112 -0
  518. fervis/observability/render.py +90 -0
  519. fervis/observability/usage.py +318 -0
  520. fervis/observability/usage_types.py +23 -0
  521. fervis/project/__init__.py +50 -0
  522. fervis/project/auth_config/__init__.py +14 -0
  523. fervis/project/auth_config/checks.py +195 -0
  524. fervis/project/auth_config/commands.py +318 -0
  525. fervis/project/auth_config/loading.py +42 -0
  526. fervis/project/auth_config/validation.py +235 -0
  527. fervis/project/catalog_checks.py +176 -0
  528. fervis/project/catalog_command.py +269 -0
  529. fervis/project/catalog_errors.py +54 -0
  530. fervis/project/config_commands.py +470 -0
  531. fervis/project/config_io.py +157 -0
  532. fervis/project/config_schema.py +456 -0
  533. fervis/project/config_versions/__init__.py +11 -0
  534. fervis/project/config_versions/auth.py +244 -0
  535. fervis/project/config_versions/common.py +57 -0
  536. fervis/project/config_versions/main.py +282 -0
  537. fervis/project/configuration.py +527 -0
  538. fervis/project/discovery.py +361 -0
  539. fervis/project/django_runtime.py +41 -0
  540. fervis/project/doctor.py +304 -0
  541. fervis/project/edit_result.py +36 -0
  542. fervis/project/host_api_context.py +75 -0
  543. fervis/project/host_runtime.py +18 -0
  544. fervis/project/importing.py +226 -0
  545. fervis/project/init.py +253 -0
  546. fervis/project/integration.py +249 -0
  547. fervis/project/model_commands.py +208 -0
  548. fervis/project/mounting/__init__.py +81 -0
  549. fervis/project/mounting/bindings.py +435 -0
  550. fervis/project/mounting/common.py +207 -0
  551. fervis/project/mounting/django.py +1062 -0
  552. fervis/project/mounting/fastapi.py +725 -0
  553. fervis/project/mounting/fastapi_sources.py +73 -0
  554. fervis/project/mounting/flask.py +472 -0
  555. fervis/project/mounting/source.py +163 -0
  556. fervis/project/persistence/__init__.py +100 -0
  557. fervis/project/persistence/alembic_runner.py +55 -0
  558. fervis/project/persistence/contracts.py +84 -0
  559. fervis/project/persistence/database_url.py +124 -0
  560. fervis/project/persistence/revisions.py +7 -0
  561. fervis/project/persistence/schema.py +110 -0
  562. fervis/project/persistence/schema_snapshots/__init__.py +0 -0
  563. fervis/project/persistence/schema_snapshots/v0001.py +1455 -0
  564. fervis/project/persistence/schema_snapshots/v0002.py +22 -0
  565. fervis/project/persistence/sql_backend.py +512 -0
  566. fervis/project/persistence/sqlite.py +98 -0
  567. fervis/project/persistence/sqlite_engine.py +20 -0
  568. fervis/project/persistence_migrations/env.py +22 -0
  569. fervis/project/persistence_migrations/script.py.mako +28 -0
  570. fervis/project/persistence_migrations/versions/0001_initial.py +21 -0
  571. fervis/project/persistence_migrations/versions/0002_read_context_ref.py +39 -0
  572. fervis/project/source_detection.py +59 -0
  573. fervis/project/source_paths.py +44 -0
  574. fervis/project/source_scope.py +64 -0
  575. fervis/questions/__init__.py +19 -0
  576. fervis/questions/contracts.py +231 -0
  577. fervis/questions/ports.py +285 -0
  578. fervis/questions/result_data.py +20 -0
  579. fervis/questions/service.py +456 -0
  580. fervis/run_work/__init__.py +9 -0
  581. fervis/run_work/contracts.py +30 -0
  582. fervis/run_work/events.py +174 -0
  583. fervis/run_work/queue/django/__init__.py +27 -0
  584. fervis/run_work/queue/django/apps.py +7 -0
  585. fervis/run_work/queue/django/migrations/0001_initial.py +131 -0
  586. fervis/run_work/queue/django/migrations/0002_runtime_context.py +15 -0
  587. fervis/run_work/queue/django/migrations/0003_remove_max_turns.py +14 -0
  588. fervis/run_work/queue/django/migrations/0004_read_context_ref.py +20 -0
  589. fervis/run_work/queue/django/migrations/__init__.py +1 -0
  590. fervis/run_work/queue/django/models.py +87 -0
  591. fervis/run_work/queue/django/queue.py +436 -0
  592. fervis/run_work/queue/django/runner.py +65 -0
  593. fervis/run_work/queued_execution.py +92 -0
  594. fervis/run_work/service.py +255 -0
  595. fervis/run_work/worker.py +207 -0
  596. fervis/storage/__init__.py +1 -0
  597. fervis/storage/sql/__init__.py +5 -0
  598. fervis/storage/sql/authority_scope.py +71 -0
  599. fervis/storage/sql/bundle.py +103 -0
  600. fervis/storage/sql/checks.py +162 -0
  601. fervis/storage/sql/engine.py +71 -0
  602. fervis/storage/sql/lineage_query.py +538 -0
  603. fervis/storage/sql/lineage_store.py +141 -0
  604. fervis/storage/sql/lookup_runtime.py +48 -0
  605. fervis/storage/sql/observability_query.py +276 -0
  606. fervis/storage/sql/prompt_captures.py +147 -0
  607. fervis/storage/sql/question_interface.py +16 -0
  608. fervis/storage/sql/question_run_ports.py +817 -0
  609. fervis/storage/sql/rows.py +46 -0
  610. fervis/storage/sql/terminal.py +130 -0
  611. fervis/storage/sql/transaction.py +53 -0
  612. fervis/storage/sql/work_items.py +584 -0
  613. fervis-0.1.0.dist-info/METADATA +87 -0
  614. fervis-0.1.0.dist-info/RECORD +617 -0
  615. fervis-0.1.0.dist-info/WHEEL +4 -0
  616. fervis-0.1.0.dist-info/entry_points.txt +2 -0
  617. fervis-0.1.0.dist-info/licenses/LICENSE +202 -0
fervis/__init__.py ADDED
@@ -0,0 +1,51 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ from fervis.project import (
6
+ DatabaseUrlPersistence,
7
+ DjangoAppSource,
8
+ DjangoDatabasePersistence,
9
+ DjangoIntegration,
10
+ FastAPIAppSource,
11
+ FastAPIIntegration,
12
+ FervisConfig,
13
+ FlaskAppSource,
14
+ FlaskIntegration,
15
+ HostConfig,
16
+ ModelConfig,
17
+ PersistenceTarget,
18
+ ProviderConfig,
19
+ RuntimeRoutes,
20
+ SQLitePersistence,
21
+ discover_project,
22
+ )
23
+ from fervis.project.configuration import ConfigProblem, load_fervis_project_config
24
+
25
+
26
+ def configured_fervis(*, root: Path | str | None = None):
27
+ project = discover_project(Path(root) if root is not None else Path.cwd())
28
+ loaded = load_fervis_project_config(project)
29
+ if isinstance(loaded, ConfigProblem):
30
+ raise RuntimeError(loaded.message)
31
+ return loaded.integration
32
+
33
+
34
+ __all__ = [
35
+ "DatabaseUrlPersistence",
36
+ "DjangoAppSource",
37
+ "DjangoDatabasePersistence",
38
+ "DjangoIntegration",
39
+ "FastAPIAppSource",
40
+ "FastAPIIntegration",
41
+ "FervisConfig",
42
+ "FlaskAppSource",
43
+ "FlaskIntegration",
44
+ "HostConfig",
45
+ "ModelConfig",
46
+ "PersistenceTarget",
47
+ "ProviderConfig",
48
+ "RuntimeRoutes",
49
+ "SQLitePersistence",
50
+ "configured_fervis",
51
+ ]
@@ -0,0 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+ from fervis import configured_fervis
4
+ from fervis.project import DjangoIntegration
5
+
6
+ __all__ = ["DjangoIntegration", "configured_fervis"]
fervis/django/urls.py ADDED
@@ -0,0 +1,5 @@
1
+ from __future__ import annotations
2
+
3
+ from fervis.interfaces.django.urls import urlpatterns
4
+
5
+ __all__ = ["urlpatterns"]
fervis/error_codes.py ADDED
@@ -0,0 +1,24 @@
1
+ """Fervis error codes used by framework and provider adapters."""
2
+
3
+ VALIDATION_ERROR = "validation_error"
4
+ MISSING_REQUIRED_PARAMETER = "missing_required_parameter"
5
+ INVALID_MODEL_KEY = "invalid_model_key"
6
+
7
+ INSUFFICIENT_PERMISSIONS = "insufficient_permissions"
8
+ READ_CONTEXT_REQUIRED = "read_context_required"
9
+
10
+ RATE_LIMIT_EXCEEDED = "rate_limit_exceeded"
11
+
12
+ INTERNAL_ERROR = "internal_error"
13
+ CONFIGURATION_ERROR = "configuration_error"
14
+ LLM_API_ERROR = "llm_api_error"
15
+ LLM_API_CONNECTION_ERROR = "llm_api_connection_error"
16
+ LLM_API_TIMEOUT = "llm_api_timeout"
17
+ LLM_API_BAD_REQUEST = "llm_api_bad_request"
18
+ LLM_API_AUTHENTICATION_ERROR = "llm_api_authentication_error"
19
+ LLM_API_PERMISSION_ERROR = "llm_api_permission_error"
20
+ LLM_API_NOT_FOUND = "llm_api_not_found"
21
+ LLM_API_CONFLICT = "llm_api_conflict"
22
+ LLM_API_RATE_LIMITED = "llm_api_rate_limited"
23
+ LLM_API_INTERNAL_ERROR = "llm_api_internal_error"
24
+ LLM_API_RESPONSE_INVALID = "llm_api_response_invalid"
fervis/errors.py ADDED
@@ -0,0 +1,388 @@
1
+ """Fervis-owned API error hierarchy.
2
+
3
+ This module intentionally has no Django/FastAPI/Flask dependency. Framework
4
+ adapters may translate these errors into host responses, but provider and core
5
+ runtime code should not import host application error modules.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any
11
+
12
+ from . import error_codes as codes
13
+
14
+
15
+ class APIError(Exception):
16
+ """Base exception for structured Fervis errors."""
17
+
18
+ error_type = "system"
19
+ status_code: int | None = None
20
+ retryable = False
21
+
22
+ def __init__(
23
+ self,
24
+ code: str,
25
+ message: str,
26
+ *,
27
+ developer_message: str | None = None,
28
+ context: dict[str, Any] | None = None,
29
+ details: list[dict[str, Any]] | None = None,
30
+ retry_after: int | None = None,
31
+ ) -> None:
32
+ self.code = code
33
+ self.message = message
34
+ self.developer_message = developer_message or message
35
+ self.context = dict(context or {})
36
+ self.details = list(details or [])
37
+ self.retry_after = retry_after
38
+ super().__init__(message)
39
+
40
+ @classmethod
41
+ def configuration_error(
42
+ cls, message: str = "Service configuration is invalid."
43
+ ) -> "APIError":
44
+ return build_error(cls, codes.CONFIGURATION_ERROR, message)
45
+
46
+ @classmethod
47
+ def internal_error(cls) -> "APIError":
48
+ return build_error(cls, codes.INTERNAL_ERROR, "An unexpected error occurred.")
49
+
50
+
51
+ class Validation(APIError):
52
+ error_type = "validation"
53
+ status_code = 400
54
+
55
+ @classmethod
56
+ def request_validation_failed(
57
+ cls,
58
+ *,
59
+ message: str = "Request validation failed.",
60
+ details: list[dict[str, Any]] | None = None,
61
+ ) -> "Validation":
62
+ return build_error(
63
+ cls,
64
+ codes.VALIDATION_ERROR,
65
+ message,
66
+ details=details or [],
67
+ )
68
+
69
+ @classmethod
70
+ def invalid_model_key(
71
+ cls, *, allowed_models: list[str], message: str = "Invalid model key."
72
+ ) -> "Validation":
73
+ return build_error(
74
+ cls,
75
+ codes.INVALID_MODEL_KEY,
76
+ message,
77
+ context={"allowed_models": list(allowed_models)},
78
+ )
79
+
80
+
81
+ class Authorization(APIError):
82
+ error_type = "authorization"
83
+ status_code = 403
84
+
85
+ @classmethod
86
+ def insufficient_permissions(cls) -> "Authorization":
87
+ return build_error(
88
+ cls,
89
+ codes.INSUFFICIENT_PERMISSIONS,
90
+ "You do not have permission to perform this action.",
91
+ )
92
+
93
+
94
+ class Authentication(APIError):
95
+ error_type = "authorization"
96
+ status_code = 401
97
+
98
+ @classmethod
99
+ def read_context_required(
100
+ cls,
101
+ message: str = "Fervis could not capture an authenticated read context.",
102
+ ) -> "Authentication":
103
+ return build_error(cls, codes.READ_CONTEXT_REQUIRED, message)
104
+
105
+
106
+ class NotFound(APIError):
107
+ error_type = "not_found"
108
+ status_code = 404
109
+
110
+ @classmethod
111
+ def for_resource(
112
+ cls,
113
+ resource_type: str,
114
+ resource_id: str | int | None = "unknown",
115
+ *,
116
+ message: str | None = None,
117
+ ) -> "NotFound":
118
+ return build_error(
119
+ cls,
120
+ f"{resource_type}_not_found",
121
+ message or f"{resource_type.replace('_', ' ').title()} not found.",
122
+ developer_message=f"{resource_type} {resource_id} does not exist",
123
+ context={"resource_type": resource_type, "resource_id": str(resource_id)},
124
+ )
125
+
126
+
127
+ class RateLimit(APIError):
128
+ error_type = "rate_limit"
129
+ status_code = 429
130
+ retryable = True
131
+
132
+ @classmethod
133
+ def llm_api_rate_limited(
134
+ cls,
135
+ *,
136
+ provider: str,
137
+ reason: str | None = None,
138
+ error_class: str | None = None,
139
+ context: dict[str, Any] | None = None,
140
+ retry_after: int | None = None,
141
+ ) -> "RateLimit":
142
+ return build_error(
143
+ cls,
144
+ codes.LLM_API_RATE_LIMITED,
145
+ "LLM provider rate limit exceeded.",
146
+ context=_provider_context(provider, reason, error_class, context),
147
+ retry_after=retry_after,
148
+ )
149
+
150
+
151
+ class Unavailable(APIError):
152
+ error_type = "system"
153
+ status_code = 502
154
+ retryable = True
155
+
156
+ @classmethod
157
+ def llm_api_error(
158
+ cls,
159
+ *,
160
+ provider: str,
161
+ reason: str | None = None,
162
+ error_class: str | None = None,
163
+ context: dict[str, Any] | None = None,
164
+ ) -> "Unavailable":
165
+ return cls._llm_error(
166
+ code=codes.LLM_API_ERROR,
167
+ message="LLM provider is currently unavailable.",
168
+ provider=provider,
169
+ reason=reason,
170
+ error_class=error_class,
171
+ context=context,
172
+ )
173
+
174
+ @classmethod
175
+ def llm_api_connection_error(
176
+ cls,
177
+ *,
178
+ provider: str,
179
+ reason: str | None = None,
180
+ error_class: str | None = None,
181
+ context: dict[str, Any] | None = None,
182
+ ) -> "Unavailable":
183
+ return cls._llm_error(
184
+ code=codes.LLM_API_CONNECTION_ERROR,
185
+ message="Could not connect to the LLM provider.",
186
+ provider=provider,
187
+ reason=reason,
188
+ error_class=error_class,
189
+ context=context,
190
+ )
191
+
192
+ @classmethod
193
+ def llm_api_timeout(
194
+ cls,
195
+ *,
196
+ provider: str,
197
+ reason: str | None = None,
198
+ error_class: str | None = None,
199
+ context: dict[str, Any] | None = None,
200
+ ) -> "Unavailable":
201
+ return cls._llm_error(
202
+ code=codes.LLM_API_TIMEOUT,
203
+ message="LLM provider timed out.",
204
+ provider=provider,
205
+ reason=reason,
206
+ error_class=error_class,
207
+ context=context,
208
+ )
209
+
210
+ @classmethod
211
+ def llm_api_bad_request(
212
+ cls,
213
+ *,
214
+ provider: str,
215
+ reason: str | None = None,
216
+ error_class: str | None = None,
217
+ context: dict[str, Any] | None = None,
218
+ ) -> "Unavailable":
219
+ return cls._llm_error(
220
+ code=codes.LLM_API_BAD_REQUEST,
221
+ message="LLM request was rejected by the provider.",
222
+ provider=provider,
223
+ reason=reason,
224
+ error_class=error_class,
225
+ context=context,
226
+ )
227
+
228
+ @classmethod
229
+ def llm_api_authentication_error(
230
+ cls,
231
+ *,
232
+ provider: str,
233
+ reason: str | None = None,
234
+ error_class: str | None = None,
235
+ context: dict[str, Any] | None = None,
236
+ ) -> "Unavailable":
237
+ return cls._llm_error(
238
+ code=codes.LLM_API_AUTHENTICATION_ERROR,
239
+ message="LLM provider authentication failed.",
240
+ provider=provider,
241
+ reason=reason,
242
+ error_class=error_class,
243
+ context=context,
244
+ )
245
+
246
+ @classmethod
247
+ def llm_api_permission_error(
248
+ cls,
249
+ *,
250
+ provider: str,
251
+ reason: str | None = None,
252
+ error_class: str | None = None,
253
+ context: dict[str, Any] | None = None,
254
+ ) -> "Unavailable":
255
+ return cls._llm_error(
256
+ code=codes.LLM_API_PERMISSION_ERROR,
257
+ message="LLM provider denied access to the requested resource.",
258
+ provider=provider,
259
+ reason=reason,
260
+ error_class=error_class,
261
+ context=context,
262
+ )
263
+
264
+ @classmethod
265
+ def llm_api_not_found(
266
+ cls,
267
+ *,
268
+ provider: str,
269
+ reason: str | None = None,
270
+ error_class: str | None = None,
271
+ context: dict[str, Any] | None = None,
272
+ ) -> "Unavailable":
273
+ return cls._llm_error(
274
+ code=codes.LLM_API_NOT_FOUND,
275
+ message="LLM provider resource was not found.",
276
+ provider=provider,
277
+ reason=reason,
278
+ error_class=error_class,
279
+ context=context,
280
+ )
281
+
282
+ @classmethod
283
+ def llm_api_conflict(
284
+ cls,
285
+ *,
286
+ provider: str,
287
+ reason: str | None = None,
288
+ error_class: str | None = None,
289
+ context: dict[str, Any] | None = None,
290
+ ) -> "Unavailable":
291
+ return cls._llm_error(
292
+ code=codes.LLM_API_CONFLICT,
293
+ message="LLM provider reported a request conflict.",
294
+ provider=provider,
295
+ reason=reason,
296
+ error_class=error_class,
297
+ context=context,
298
+ )
299
+
300
+ @classmethod
301
+ def llm_api_internal_error(
302
+ cls,
303
+ *,
304
+ provider: str,
305
+ reason: str | None = None,
306
+ error_class: str | None = None,
307
+ context: dict[str, Any] | None = None,
308
+ ) -> "Unavailable":
309
+ return cls._llm_error(
310
+ code=codes.LLM_API_INTERNAL_ERROR,
311
+ message="LLM provider encountered an internal error.",
312
+ provider=provider,
313
+ reason=reason,
314
+ error_class=error_class,
315
+ context=context,
316
+ )
317
+
318
+ @classmethod
319
+ def llm_api_response_invalid(
320
+ cls,
321
+ *,
322
+ provider: str,
323
+ reason: str | None = None,
324
+ error_class: str | None = None,
325
+ context: dict[str, Any] | None = None,
326
+ ) -> "Unavailable":
327
+ return cls._llm_error(
328
+ code=codes.LLM_API_RESPONSE_INVALID,
329
+ message="LLM provider returned an invalid response payload.",
330
+ provider=provider,
331
+ reason=reason,
332
+ error_class=error_class,
333
+ context=context,
334
+ )
335
+
336
+ @classmethod
337
+ def _llm_error(
338
+ cls,
339
+ *,
340
+ code: str,
341
+ message: str,
342
+ provider: str,
343
+ reason: str | None = None,
344
+ error_class: str | None = None,
345
+ context: dict[str, Any] | None = None,
346
+ ) -> "Unavailable":
347
+ return build_error(
348
+ cls,
349
+ code,
350
+ message,
351
+ context=_provider_context(provider, reason, error_class, context),
352
+ )
353
+
354
+
355
+ def build_error(
356
+ exception_cls: type[APIError],
357
+ code: str,
358
+ message: str,
359
+ *,
360
+ context: dict[str, Any] | None = None,
361
+ developer_message: str | None = None,
362
+ details: list[dict[str, Any]] | None = None,
363
+ retry_after: int | None = None,
364
+ ) -> APIError:
365
+ return exception_cls(
366
+ code=code,
367
+ message=message,
368
+ developer_message=developer_message,
369
+ context=context,
370
+ details=details,
371
+ retry_after=retry_after,
372
+ )
373
+
374
+
375
+ def _provider_context(
376
+ provider: str,
377
+ reason: str | None,
378
+ error_class: str | None,
379
+ context: dict[str, Any] | None,
380
+ ) -> dict[str, Any]:
381
+ payload = {
382
+ "provider": provider,
383
+ "reason": reason,
384
+ "error_class": error_class,
385
+ }
386
+ if context:
387
+ payload.update(context)
388
+ return payload
@@ -0,0 +1 @@
1
+ """Evaluation tooling for portable Fervis runtime checks."""
@@ -0,0 +1,11 @@
1
+ from __future__ import annotations
2
+
3
+ from fervis import configured_fervis
4
+ from fervis.interfaces.fastapi import fervis_fastapi_router
5
+ from fervis.project import FastAPIIntegration
6
+
7
+ __all__ = [
8
+ "FastAPIIntegration",
9
+ "configured_fervis",
10
+ "fervis_fastapi_router",
11
+ ]
@@ -0,0 +1,11 @@
1
+ from __future__ import annotations
2
+
3
+ from fervis import configured_fervis
4
+ from fervis.interfaces.flask import fervis_flask_blueprint
5
+ from fervis.project import FlaskIntegration
6
+
7
+ __all__ = [
8
+ "FlaskIntegration",
9
+ "configured_fervis",
10
+ "fervis_flask_blueprint",
11
+ ]
File without changes
File without changes
@@ -0,0 +1 @@
1
+ """Django + DRF host API adapter package."""
@@ -0,0 +1,99 @@
1
+ """Django framework adapter for Fervis endpoint access."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from fervis.host_api.contracts import EndpointContract
8
+ from fervis.host_api.adapters.http import (
9
+ execute_http_read,
10
+ http_read_config_from_auth_schema,
11
+ )
12
+ from fervis.host_api.contracts.authority import ReadAuthority, ReadContextRef
13
+ from fervis.host_api.contracts.credentials import DelegatedReadCredential
14
+ from fervis.host_api.contracts.read import ReadInvocation
15
+ from fervis.host_api.credentials import (
16
+ credential_overlay_from_auth_schema,
17
+ delegated_credential_from_request,
18
+ )
19
+ from fervis.host_api.contracts.ports import (
20
+ EndpointExecutionError,
21
+ EndpointExecutionResult,
22
+ )
23
+ from fervis.lookup.relation_catalog.from_host_api import (
24
+ relation_catalog_from_endpoint_contracts,
25
+ )
26
+ from fervis.lookup.relation_catalog import RelationCatalog
27
+ from fervis.project.source_scope import DjangoSourceScope
28
+
29
+ from .catalog import get_endpoint_contracts
30
+ from .catalog import get_endpoint_contract
31
+ from .principal import capture_django_read_context, resolve_django_read_context_ref
32
+ from .executor import execute_get_endpoint
33
+
34
+
35
+ class DjangoHostApiAdapter:
36
+ def __init__(
37
+ self,
38
+ *,
39
+ sources: tuple[DjangoSourceScope, ...],
40
+ auth_schema: dict[str, object] | None = None,
41
+ ):
42
+ self.sources = tuple(sources)
43
+ self.auth_schema = auth_schema
44
+
45
+ def describe_sources(self) -> tuple[EndpointContract, ...]:
46
+ return get_endpoint_contracts(sources=self.sources)
47
+
48
+ def build_relation_catalog(self) -> RelationCatalog:
49
+ return relation_catalog_from_endpoint_contracts(self.describe_sources())
50
+
51
+ def capture_read_context(self, request: Any) -> ReadContextRef:
52
+ return capture_django_read_context(request)
53
+
54
+ def capture_delegated_credential(
55
+ self,
56
+ request: Any,
57
+ ) -> DelegatedReadCredential | None:
58
+ return delegated_credential_from_request(
59
+ schema=self.auth_schema,
60
+ request=request,
61
+ )
62
+
63
+ def execute_read(
64
+ self,
65
+ *,
66
+ authority: ReadAuthority,
67
+ invocation: ReadInvocation,
68
+ ) -> EndpointExecutionResult:
69
+ http_config = http_read_config_from_auth_schema(self.auth_schema)
70
+ if http_config is not None:
71
+ contract = get_endpoint_contract(
72
+ invocation.endpoint_name,
73
+ sources=self.sources,
74
+ )
75
+ if contract is None:
76
+ raise EndpointExecutionError(
77
+ f"Unknown endpoint: {invocation.endpoint_name}"
78
+ )
79
+ return execute_http_read(
80
+ contract=contract,
81
+ authority=authority,
82
+ invocation=invocation,
83
+ config=http_config,
84
+ )
85
+ user = resolve_django_read_context_ref(authority.read_context_ref)
86
+ return execute_get_endpoint(
87
+ endpoint_name=invocation.endpoint_name,
88
+ user=user,
89
+ sources=self.sources,
90
+ path_params=dict(invocation.path_params),
91
+ query_params=dict(invocation.query_params),
92
+ page_policy=(
93
+ None if invocation.page_policy is None else dict(invocation.page_policy)
94
+ ),
95
+ transport_overlay=credential_overlay_from_auth_schema(
96
+ schema=self.auth_schema,
97
+ credential=authority.delegated_credential,
98
+ ),
99
+ )