deriva-cli 0.8.3__tar.gz → 0.8.4__tar.gz

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 (203) hide show
  1. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/PKG-INFO +3 -3
  2. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/grafeo/__init__.py +8 -0
  3. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/grafeo/manager.py +51 -0
  4. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/graph/manager.py +3 -3
  5. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/graph/metamodel.py +43 -1
  6. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/llm/manager.py +81 -7
  7. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/llm/model_registry.py +20 -3
  8. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/llm/models.py +7 -3
  9. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/llm/retry.py +5 -1
  10. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/nlp/translate.py +1 -2
  11. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/repository/manager.py +155 -12
  12. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/repository/models.py +2 -0
  13. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/csharp.py +2 -2
  14. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/javascript.py +25 -14
  15. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/python.py +5 -3
  16. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/manager.py +1 -1
  17. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/cli/cli.py +83 -7
  18. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/cli/commands/config.py +41 -1
  19. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/cli/commands/graph.py +1 -0
  20. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/cli/commands/repo.py +51 -4
  21. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/cli/commands/run.py +3 -2
  22. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/file_utils.py +28 -1
  23. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/settings.py +8 -1
  24. deriva_cli-0.8.4/deriva/common/urls.py +15 -0
  25. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/component_services.py +12 -2
  26. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/element_base.py +1 -1
  27. deriva_cli-0.8.4/deriva/modules/derivation/quality.py +107 -0
  28. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/joint_consistency.py +28 -16
  29. deriva_cli-0.8.4/deriva/modules/extraction/coverage.py +126 -0
  30. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/data_domains.py +5 -1
  31. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/directory_classification.py +4 -1
  32. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/edges.py +2 -1
  33. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/__init__.py +5 -2
  34. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/config.py +56 -0
  35. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/extraction.py +115 -16
  36. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/model_configs.py +70 -2
  37. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/ontology.py +30 -16
  38. deriva_cli-0.8.4/deriva/services/quality.py +47 -0
  39. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/session.py +204 -14
  40. deriva_cli-0.8.4/deriva/services/storage.py +92 -0
  41. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/pyproject.toml +3 -3
  42. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/pyproject.toml.orig +3 -3
  43. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/LICENSE +0 -0
  44. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/README.md +0 -0
  45. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/__init__.py +0 -0
  46. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/documentation/Diagram.html +0 -0
  47. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/documentation/View.html +0 -0
  48. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/documentation/model.html +0 -0
  49. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/examples/archimate3_Diagram.xsd +0 -0
  50. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/examples/archimate3_Model.xsd +0 -0
  51. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/examples/archimate3_View.xsd +0 -0
  52. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/schemas/archimate3_Diagram.xsd +0 -0
  53. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/schemas/archimate3_Model.xsd +0 -0
  54. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/schemas/archimate3_View.xsd +0 -0
  55. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-1.xml +0 -0
  56. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-10.xml +0 -0
  57. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-2.xml +0 -0
  58. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-3.xml +0 -0
  59. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-4.xml +0 -0
  60. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-5.xml +0 -0
  61. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-7.xml +0 -0
  62. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-8.xml +0 -0
  63. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/archimate_xsd/snippets/Interoperability testing snippet-9.xml +0 -0
  64. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/manager.py +0 -0
  65. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/models.py +0 -0
  66. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/validation.py +0 -0
  67. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/views.py +0 -0
  68. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/archimate/xml_export.py +0 -0
  69. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/README.md +0 -0
  70. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/__init__.py +0 -0
  71. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/data/derivation_config.json +0 -0
  72. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/data/derivation_patterns.json +0 -0
  73. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/data/extraction_config.json +0 -0
  74. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/data/file_types.json +0 -0
  75. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/data/function_models/pcf-8.0-cross-industry.csv +0 -0
  76. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/data/system_settings.json +0 -0
  77. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/db_tool.py +0 -0
  78. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/manager.py +0 -0
  79. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/scripts/7_extraction_params.sql +0 -0
  80. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/database/scripts/schema.sql +0 -0
  81. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/graph/README.md +0 -0
  82. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/graph/__init__.py +0 -0
  83. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/graph/cache.py +0 -0
  84. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/graph/models.py +0 -0
  85. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/llm/README.md +0 -0
  86. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/llm/__init__.py +0 -0
  87. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/llm/cache.py +0 -0
  88. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/llm/rate_limiter.py +0 -0
  89. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/llm/schemas.py +0 -0
  90. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/nlp/__init__.py +0 -0
  91. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/nlp/extract.py +0 -0
  92. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/nlp/language.py +0 -0
  93. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/nlp/manager.py +0 -0
  94. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/nlp/models.py +0 -0
  95. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/nlp/phrases.py +0 -0
  96. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/nlp/settings.py +0 -0
  97. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/nlp/text.py +0 -0
  98. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/repository/README.md +0 -0
  99. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/repository/__init__.py +0 -0
  100. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/__init__.py +0 -0
  101. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/cdk.py +0 -0
  102. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/__init__.py +0 -0
  103. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/base.py +0 -0
  104. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/bms.py +0 -0
  105. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/cobol.py +0 -0
  106. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/java.py +0 -0
  107. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/jcl.py +0 -0
  108. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/sql.py +0 -0
  109. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/languages/structural.py +0 -0
  110. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/adapters/treesitter/models.py +0 -0
  111. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/cli/README.md +0 -0
  112. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/cli/__init__.py +0 -0
  113. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/cli/commands/__init__.py +0 -0
  114. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/cli/progress.py +0 -0
  115. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/__init__.py +0 -0
  116. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/cache_utils.py +0 -0
  117. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/chunking.py +0 -0
  118. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/document_reader.py +0 -0
  119. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/exceptions.py +0 -0
  120. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/json_utils.py +0 -0
  121. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/llm_utils.py +0 -0
  122. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/logging.py +0 -0
  123. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/naming.py +0 -0
  124. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/ocel.py +0 -0
  125. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/paths.py +0 -0
  126. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/schema_utils.py +0 -0
  127. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/time_utils.py +0 -0
  128. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/timing.py +0 -0
  129. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/common/types.py +0 -0
  130. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/__init__.py +0 -0
  131. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/__init__.py +0 -0
  132. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/consistency.py +0 -0
  133. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/conventions.py +0 -0
  134. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/cross_repo_analysis.py +0 -0
  135. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/deviation.py +0 -0
  136. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/fit_analysis.py +0 -0
  137. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/model_quality.py +0 -0
  138. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/run_consistency.py +0 -0
  139. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/semantic_matching.py +0 -0
  140. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/stability_analysis.py +0 -0
  141. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/analysis/types.py +0 -0
  142. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/__init__.py +0 -0
  143. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/application_component.py +0 -0
  144. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/application_interface.py +0 -0
  145. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/application_service.py +0 -0
  146. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/base.py +0 -0
  147. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/business_actor.py +0 -0
  148. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/business_event.py +0 -0
  149. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/business_function.py +0 -0
  150. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/business_object.py +0 -0
  151. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/business_process.py +0 -0
  152. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/data_object.py +0 -0
  153. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/detail_level.py +0 -0
  154. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/device.py +0 -0
  155. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/function_model.py +0 -0
  156. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/levels.py +0 -0
  157. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/node.py +0 -0
  158. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/prep.py +0 -0
  159. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/__init__.py +0 -0
  160. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/abstraction_levels.py +0 -0
  161. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/base.py +0 -0
  162. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/cross_layer.py +0 -0
  163. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/duplicate_elements.py +0 -0
  164. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/duplicate_relationships.py +0 -0
  165. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/graph_relationships.py +0 -0
  166. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/joint.py +0 -0
  167. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/normalization.py +0 -0
  168. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/orphan_elements.py +0 -0
  169. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/refine/structural_consistency.py +0 -0
  170. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/system_software.py +0 -0
  171. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/derivation/technology_service.py +0 -0
  172. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/__init__.py +0 -0
  173. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/base.py +0 -0
  174. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/build_modules.py +0 -0
  175. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/business_concept.py +0 -0
  176. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/classification.py +0 -0
  177. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/concept_candidates.py +0 -0
  178. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/cooperation.py +0 -0
  179. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/data_entities.py +0 -0
  180. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/deployable_units.py +0 -0
  181. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/deployments.py +0 -0
  182. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/directory.py +0 -0
  183. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/external_dependency.py +0 -0
  184. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/file.py +0 -0
  185. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/lifecycles.py +0 -0
  186. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/method.py +0 -0
  187. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/repository.py +0 -0
  188. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/roles.py +0 -0
  189. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/step_tags.py +0 -0
  190. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/technologies.py +0 -0
  191. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/technology_candidates.py +0 -0
  192. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/test.py +0 -0
  193. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/modules/extraction/type_definition.py +0 -0
  194. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/about.py +0 -0
  195. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/config_models.py +0 -0
  196. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/derivation.py +0 -0
  197. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/export_bundle.py +0 -0
  198. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/export_levels.py +0 -0
  199. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/llm_log.py +0 -0
  200. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/overfit.py +0 -0
  201. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/pipeline.py +0 -0
  202. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/deriva/services/trace.py +0 -0
  203. {deriva_cli-0.8.3 → deriva_cli-0.8.4}/packages/README-deriva-cli.md +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: deriva-cli
3
- Version: 0.8.3
3
+ Version: 0.8.4
4
4
  Summary: Deriva is a research project aimed at enabling digital architects by automating the derivation of enterprise architecture models. Deriva uses dual knowledge graphs, heuristics and LLM's.
5
5
  Keywords: archimate,enterprise-architecture,code-analysis,llm,grafeo,fastapi
6
6
  Author: S. T. Grond
@@ -38,8 +38,8 @@ Requires-Dist: spacy==3.8.16
38
38
  Requires-Dist: ctranslate2==4.8.3
39
39
  Requires-Dist: sentencepiece==0.2.2
40
40
  Requires-Dist: solvor>=0.6.3
41
- Requires-Dist: pydantic-ai-slim[openai,anthropic]>=2.55
42
- Requires-Dist: httpx2>=2.13
41
+ Requires-Dist: pydantic-ai-slim[openai,anthropic]>=2.57
42
+ Requires-Dist: httpx2>=2.14
43
43
  Requires-Dist: lmdb>=1.6
44
44
  Requires-Dist: typer>=0.27
45
45
  Requires-Dist: tree-sitter-typescript==0.23.2
@@ -21,9 +21,13 @@ from .manager import (
21
21
  DEFAULT_DATABASE,
22
22
  GrafeoConnection,
23
23
  close_database,
24
+ database_bytes,
25
+ database_exists,
24
26
  database_file,
27
+ delete_database,
25
28
  engine_info,
26
29
  get_database,
30
+ list_databases,
27
31
  use_database,
28
32
  )
29
33
 
@@ -31,8 +35,12 @@ __all__ = [
31
35
  "DEFAULT_DATABASE",
32
36
  "GrafeoConnection",
33
37
  "close_database",
38
+ "database_bytes",
39
+ "database_exists",
34
40
  "database_file",
41
+ "delete_database",
35
42
  "engine_info",
36
43
  "get_database",
44
+ "list_databases",
37
45
  "use_database",
38
46
  ]
@@ -27,8 +27,10 @@ Usage:
27
27
  from __future__ import annotations
28
28
 
29
29
  import logging
30
+ import shutil
30
31
  import time
31
32
  import weakref
33
+ from pathlib import Path
32
34
  from typing import Any
33
35
 
34
36
  from deriva.common.paths import resolve
@@ -77,6 +79,55 @@ def database_file(key: str) -> str | None:
77
79
  return str(folder / f"{key}.grafeo")
78
80
 
79
81
 
82
+ def _stored_paths(key: str) -> tuple[Path, Path] | None:
83
+ """The key's database file and its write-ahead log folder, never created (None in memory or for an invalid key)."""
84
+ directory = GrafeoSettings().db_dir
85
+ if not directory or not key or key in (".", "..") or any(sep in key for sep in ("/", "\\")):
86
+ return None
87
+ folder = resolve(directory)
88
+ return folder / f"{key}.grafeo", folder / f"{key}.grafeo.wal"
89
+
90
+
91
+ def database_exists(key: str) -> bool:
92
+ """Whether the key's database file exists; never creates it or its folder (False in memory or for an invalid key)."""
93
+ paths = _stored_paths(key)
94
+ return paths is not None and paths[0].exists()
95
+
96
+
97
+ def list_databases() -> list[str]:
98
+ """The keys with a database file in GRAFEO_DB_DIR, sorted (none in memory)."""
99
+ directory = GrafeoSettings().db_dir
100
+ folder = resolve(directory) if directory else None
101
+ if folder is None or not folder.is_dir():
102
+ return []
103
+ return sorted(path.name.removesuffix(".grafeo") for path in folder.glob("*.grafeo") if path.is_file())
104
+
105
+
106
+ def database_bytes(key: str) -> int:
107
+ """Bytes the key's database takes on disk: its file and its write-ahead log (0 when it has none)."""
108
+ paths = _stored_paths(key)
109
+ if paths is None:
110
+ return 0
111
+ database, log = paths
112
+ size = database.stat().st_size if database.is_file() else 0
113
+ return size + (sum(f.stat().st_size for f in log.rglob("*") if f.is_file()) if log.is_dir() else 0)
114
+
115
+
116
+ def delete_database(key: str) -> bool:
117
+ """Delete the key's database file and log; False when it had none. The active database is never deleted (switch
118
+ away first): a query after the delete would create a new, empty file."""
119
+ if key == _db_key:
120
+ raise ValueError(f"The database {key!r} is open; switch to another one before deleting it")
121
+ paths = _stored_paths(key)
122
+ if paths is None or not (paths[0].exists() or paths[1].exists()):
123
+ return False
124
+ database, log = paths
125
+ database.unlink(missing_ok=True)
126
+ if log.is_dir():
127
+ shutil.rmtree(log)
128
+ return True
129
+
130
+
80
131
  def get_database() -> Any:
81
132
  """Get or open the active GrafeoDB instance (see ``use_database``).
82
133
 
@@ -26,7 +26,7 @@ from __future__ import annotations
26
26
 
27
27
  import logging
28
28
  import re
29
- from collections.abc import Iterator
29
+ from collections.abc import Generator
30
30
  from contextlib import contextmanager
31
31
  from dataclasses import dataclass, field
32
32
  from typing import Any
@@ -280,7 +280,7 @@ class GraphManager:
280
280
  raise
281
281
 
282
282
  @contextmanager
283
- def step_tags(self, step: str, version: int | None = None) -> Iterator[None]:
283
+ def step_tags(self, step: str, version: int | None = None) -> Generator[None]:
284
284
  """Nodes and edges written inside the block carry the extraction step (``extractStep``) and its version."""
285
285
  previous = self._step_tags
286
286
  self._step_tags = {"extractStep": step, **({"extractVersion": version} if version is not None else {})}
@@ -290,7 +290,7 @@ class GraphManager:
290
290
  self._step_tags = previous
291
291
 
292
292
  @contextmanager
293
- def recording(self) -> Iterator[GraphWrites]:
293
+ def recording(self) -> Generator[GraphWrites]:
294
294
  """The nodes and edges written inside the block (for the run log); failed writes are left out."""
295
295
  recorder = GraphWrites()
296
296
  self._recorders.append(recorder)
@@ -13,6 +13,10 @@ from typing import Any
13
13
  from . import models
14
14
 
15
15
  __all__ = [
16
+ "GROUP_ORDER",
17
+ "NODE_GROUPS",
18
+ "node_description",
19
+ "node_group",
16
20
  "get_all_node_classes",
17
21
  "get_node_properties",
18
22
  "get_relationship_types",
@@ -36,7 +40,45 @@ def get_all_node_classes() -> dict[str, type]:
36
40
  return node_classes
37
41
 
38
42
 
39
- def get_node_properties(node_class: type) -> list[str]:
43
+ # The intermediate ontology's groups (as ArchiMate has its layers): where a node type's evidence comes from
44
+ GROUP_ORDER = ("structure", "code", "business", "deployment")
45
+ NODE_GROUPS = {
46
+ "Repository": "structure",
47
+ "Directory": "structure",
48
+ "File": "structure",
49
+ "Module": "structure",
50
+ "BuildModule": "structure",
51
+ "TypeDefinition": "code",
52
+ "Method": "code",
53
+ "Test": "code",
54
+ "ExternalDependency": "code",
55
+ "Service": "code",
56
+ "BusinessConcept": "business",
57
+ "DataEntity": "business",
58
+ "DataDomain": "business",
59
+ "Lifecycle": "business",
60
+ "LifecycleEvent": "business",
61
+ "Role": "business",
62
+ "DeployableUnit": "deployment",
63
+ "Deployment": "deployment",
64
+ "Environment": "deployment",
65
+ "Software": "deployment",
66
+ "Technology": "deployment",
67
+ }
68
+
69
+
70
+ def node_group(node_type: str) -> str:
71
+ """The group of a node type (``other`` for a label the metamodel does not name)."""
72
+ return NODE_GROUPS.get(node_type, "other")
73
+
74
+
75
+ def node_description(node_class: type) -> str:
76
+ """The first line of a node class's docstring."""
77
+ lines = (node_class.__doc__ or "").strip().splitlines()
78
+ return lines[0].strip() if lines else ""
79
+
80
+
81
+ def get_node_properties(node_class: type[Any]) -> list[str]:
40
82
  """Get all property names for a node class.
41
83
 
42
84
  Args:
@@ -43,8 +43,9 @@ from typing import Any, TypeVar, cast, overload
43
43
 
44
44
  import httpx2
45
45
  from pydantic import BaseModel
46
- from pydantic_ai import Agent
47
- from pydantic_ai.exceptions import ModelHTTPError
46
+ from pydantic_ai import Agent, capture_run_messages
47
+ from pydantic_ai.exceptions import IncompleteToolCall, ModelHTTPError
48
+ from pydantic_ai.messages import ModelResponse
48
49
  from pydantic_ai.settings import ModelSettings
49
50
 
50
51
  from deriva.common import settings
@@ -53,7 +54,7 @@ from deriva.common.paths import resolve
53
54
  from deriva.common.settings import LlmSettings
54
55
 
55
56
  from .cache import CacheManager
56
- from .model_registry import VALID_PROVIDERS, get_pydantic_ai_model, parse_sampling, reasoning_settings
57
+ from .model_registry import VALID_PROVIDERS, default_sampling, get_pydantic_ai_model, parse_sampling, reasoning_settings
57
58
  from .models import (
58
59
  BenchmarkModelConfig,
59
60
  CachedResponse,
@@ -214,9 +215,10 @@ def load_benchmark_models() -> dict[str, BenchmarkModelConfig]:
214
215
  api_url=fields["url"],
215
216
  api_key=fields["key"],
216
217
  api_key_env=fields["key_env"],
217
- sampling=parse_sampling(fields.get("sampling")),
218
+ sampling=parse_sampling(fields.get("sampling"), provider.lower(), model),
218
219
  reasoning=(fields.get("reasoning") or "").strip().lower() or None,
219
220
  temperature=float(own) if (own := (fields.get("temperature") or "").strip()) else None,
221
+ context=int(size) if (size := (fields.get("context") or "").strip()) else None,
220
222
  )
221
223
  except ValueError:
222
224
  continue
@@ -224,6 +226,22 @@ def load_benchmark_models() -> dict[str, BenchmarkModelConfig]:
224
226
  return configs
225
227
 
226
228
 
229
+ def _key_variable(config: BenchmarkModelConfig) -> str:
230
+ """The variable a model config's key is read from: its KEY_ENV reference, or its own LLM_{NAME}_KEY."""
231
+ return config.api_key_env or f"LLM_{config.name.upper().replace('-', '_')}_KEY"
232
+
233
+
234
+ def _has_cut_response(history: Any) -> bool:
235
+ """Whether a model response in a run's messages stopped at the output limit (finish reason ``length``)."""
236
+ return any(isinstance(m, ModelResponse) and m.finish_reason == "length" for m in history or [])
237
+
238
+
239
+ def _was_cut(result: Any) -> bool:
240
+ """Whether a model response in the run's result stopped at the output limit."""
241
+ messages = getattr(result, "all_messages", None)
242
+ return _has_cut_response(messages() if callable(messages) else [])
243
+
244
+
227
245
  def _serialize_output(output: object) -> str:
228
246
  """Serialize a pydantic model or arbitrary output to a JSON string."""
229
247
  dump = getattr(output, "model_dump_json", None)
@@ -314,6 +332,7 @@ class LLMManager:
314
332
  self.max_tokens = self.config.get("max_tokens")
315
333
  self.sampling = self.config.get("sampling", True)
316
334
  self.reasoning = self.config.get("reasoning")
335
+ self.context = self.config.get("context")
317
336
 
318
337
  @classmethod
319
338
  def from_config(
@@ -349,6 +368,8 @@ class LLMManager:
349
368
  "provider": config.provider,
350
369
  "api_url": config.get_api_url(),
351
370
  "api_key": config.get_api_key(),
371
+ "api_key_variable": _key_variable(config),
372
+ "label": config.name,
352
373
  "model": config.model,
353
374
  "cache_dir": cache_dir,
354
375
  "max_retries": max_retries,
@@ -357,6 +378,7 @@ class LLMManager:
357
378
  "nocache": nocache,
358
379
  "sampling": config.sampling,
359
380
  "reasoning": config.reasoning,
381
+ "context": config.context,
360
382
  }
361
383
 
362
384
  instance._validate_config()
@@ -389,6 +411,7 @@ class LLMManager:
389
411
  instance.max_tokens = None
390
412
  instance.sampling = config.sampling
391
413
  instance.reasoning = config.reasoning
414
+ instance.context = config.context
392
415
 
393
416
  return instance
394
417
 
@@ -397,6 +420,22 @@ class LLMManager:
397
420
  """Get the provider name."""
398
421
  return self.config.get("provider", "unknown")
399
422
 
423
+ def _warn_on_a_full_context(self, result: Any) -> None:
424
+ """A call whose last request used the model's whole context (when the config names it) lost part of its prompt
425
+ or answer: a local server such as Ollama cuts a prompt longer than its window without an error."""
426
+ if not self.context:
427
+ return
428
+ usage = getattr(getattr(result, "response", None), "usage", None)
429
+ used = (getattr(usage, "input_tokens", 0) or 0) + (getattr(usage, "output_tokens", 0) or 0)
430
+ if used >= self.context:
431
+ logger.warning(
432
+ "An LLM call used %d tokens of %s's %d-token context: part of the prompt or the answer was cut. "
433
+ "Raise the server's context (Ollama: OLLAMA_CONTEXT_LENGTH) and the model's context setting",
434
+ used,
435
+ self.model,
436
+ self.context,
437
+ )
438
+
400
439
  def _redact(self, text: str) -> str:
401
440
  """Keep the configured API key out of exception text: h11 quotes header values in its errors."""
402
441
  key = self.config.get("api_key")
@@ -417,11 +456,13 @@ class LLMManager:
417
456
  provider = config.provider
418
457
  api_url = config.get_api_url()
419
458
  api_key = config.get_api_key()
459
+ key_variable, label = _key_variable(config), config.name
420
460
  model = config.model
421
- sampling, reasoning = config.sampling, config.reasoning
461
+ sampling, reasoning, context = config.sampling, config.reasoning, config.context
422
462
  else:
423
463
  provider = llm.provider
424
- sampling, reasoning = True, None
464
+ reasoning, context = None, None
465
+ key_variable, label = f"LLM_{(provider or '').upper()}_API_KEY", provider
425
466
 
426
467
  if provider == "azure":
427
468
  api_url = llm.azure_api_url
@@ -449,6 +490,7 @@ class LLMManager:
449
490
  model = llm.lmstudio_model
450
491
  else:
451
492
  raise ConfigurationError(f"Unknown LLM provider: {provider}")
493
+ sampling = default_sampling(provider, model)
452
494
 
453
495
  max_tokens = llm.max_tokens
454
496
 
@@ -456,6 +498,8 @@ class LLMManager:
456
498
  "provider": provider,
457
499
  "api_url": api_url,
458
500
  "api_key": api_key,
501
+ "api_key_variable": key_variable,
502
+ "label": label,
459
503
  "model": model,
460
504
  "cache_dir": llm.cache_dir,
461
505
  "cache_ttl": llm.cache_ttl,
@@ -466,6 +510,7 @@ class LLMManager:
466
510
  "nocache": llm.nocache,
467
511
  "sampling": sampling,
468
512
  "reasoning": reasoning,
513
+ "context": context,
469
514
  "requests_per_minute": llm.rate_limit_rpm,
470
515
  "min_request_delay": llm.rate_limit_delay,
471
516
  # Retry configuration
@@ -494,6 +539,12 @@ class LLMManager:
494
539
  required_fields = ["provider", "api_key", "model"]
495
540
 
496
541
  missing = [f for f in required_fields if not self.config.get(f)]
542
+ # The variable to set and the .env this install reads, never a value
543
+ if "api_key" in missing and self.config.get("api_key_variable"):
544
+ raise ConfigurationError(
545
+ f"The model config {self.config.get('label') or provider} has no API key: set {self.config['api_key_variable']} "
546
+ f"in {settings.env_file()} or in the environment (General in the studio saves a key there)"
547
+ )
497
548
  if missing:
498
549
  raise ConfigurationError(f"Missing required config fields: {', '.join(missing)}")
499
550
 
@@ -555,7 +606,7 @@ class LLMManager:
555
606
  max_delay = self.config.get("retry_max_delay", 60.0)
556
607
  for attempt in range(self.max_retries + 1):
557
608
  try:
558
- return agent.run_sync(prompt, model_settings=settings), attempt
609
+ return self._run_once(agent, prompt, settings), attempt
559
610
  except Exception as e:
560
611
  if attempt == self.max_retries or not _is_retriable(e):
561
612
  raise
@@ -579,6 +630,19 @@ class LLMManager:
579
630
  time.sleep(delay)
580
631
  raise AssertionError("unreachable")
581
632
 
633
+ @staticmethod
634
+ def _run_once(agent: Agent[None, Any], prompt: str, settings: ModelSettings) -> Any:
635
+ """One agent run. pydantic-ai asks again after an answer cut at the output limit; when those answers fail
636
+ too it raises "Exceeded maximum output retries", which is the cut: raised as IncompleteToolCall."""
637
+ with capture_run_messages() as messages:
638
+ try:
639
+ return agent.run_sync(prompt, model_settings=settings)
640
+ except Exception as e:
641
+ if not isinstance(e, IncompleteToolCall) and _has_cut_response(messages):
642
+ limit = settings.get("max_tokens") or "provider default"
643
+ raise IncompleteToolCall(f"Model token limit ({limit}) reached: the answer was cut, and the answers after it failed ({e})") from e
644
+ raise
645
+
582
646
  def _agent(self, output_type: Any, system_prompt: str) -> Agent[None, Any]:
583
647
  """Reusable agent for this output type and system prompt, per thread (created lazily).
584
648
 
@@ -756,6 +820,16 @@ class LLMManager:
756
820
  "total_tokens": run_usage.input_tokens + run_usage.output_tokens,
757
821
  }
758
822
 
823
+ self._warn_on_a_full_context(result)
824
+
825
+ # A structured answer cut at the output limit is asked again by pydantic-ai, and a shorter answer that
826
+ # validates can leave part of the question out: a failure (never cached), which a caller can split
827
+ if output_type is not str and _was_cut(result):
828
+ call["error_type"] = "IncompleteToolCall"
829
+ message = f"Model token limit ({effective_max_tokens or 'provider default'}) reached: the answer was cut, and the shorter answer after it may leave part of it out"
830
+ logger.warning("LLM answer cut at the output limit (%s)", self.model)
831
+ return FailedResponse(prompt=prompt, model=self.model, error=message, error_type="IncompleteToolCall")
832
+
759
833
  # Handle response
760
834
  output: Any = result.output
761
835
 
@@ -29,10 +29,21 @@ PROVIDER_REASONING_LEVELS = {
29
29
  _SAMPLING_VALUES = {"true": True, "1": True, "yes": True, "on": True, "false": False, "0": False, "no": False, "off": False}
30
30
 
31
31
 
32
- def parse_sampling(value: str | None) -> bool:
33
- """LLM_{NAME}_SAMPLING: off for models that take no temperature (unset means on)."""
32
+ def default_sampling(provider: str, model: str) -> bool:
33
+ """Whether a model takes a temperature when its config does not say. pydantic-ai's model profile knows the Anthropic
34
+ models that refuse sampling settings (it drops them, with a warning on every call); any other model takes one."""
35
+ if provider == "anthropic":
36
+ from pydantic_ai.profiles.anthropic import anthropic_model_profile
37
+
38
+ profile = anthropic_model_profile(model) or {}
39
+ return not profile.get("anthropic_disallows_sampling_settings", False)
40
+ return True
41
+
42
+
43
+ def parse_sampling(value: str | None, provider: str = "", model: str = "") -> bool:
44
+ """LLM_{NAME}_SAMPLING: off for models that take no temperature (unset: what the model's profile says)."""
34
45
  if value is None or not value.strip():
35
- return True
46
+ return default_sampling(provider, model)
36
47
  if value.strip().lower() not in _SAMPLING_VALUES:
37
48
  raise ValueError(f"sampling must be true or false, got {value!r}")
38
49
  return _SAMPLING_VALUES[value.strip().lower()]
@@ -126,6 +137,12 @@ def get_pydantic_ai_model(config: dict[str, Any]) -> Model | str:
126
137
  from pydantic_ai.models.openai import OpenAIChatModel
127
138
  from pydantic_ai.providers.azure import AzureProvider
128
139
 
140
+ # Azure's v1 API (<resource>/openai/v1/) is OpenAI's and takes no api_version: the OpenAI client goes there
141
+ if api_url and "/openai/v1" in api_url:
142
+ from pydantic_ai.providers.openai import OpenAIProvider
143
+
144
+ base_url = api_url[: api_url.index("/openai/v1") + len("/openai/v1")]
145
+ return OpenAIChatModel(model, provider=OpenAIProvider(base_url=base_url, api_key=api_key))
129
146
  azure_endpoint = _normalize_azure_url(api_url) if api_url else None
130
147
  azure_provider = AzureProvider(
131
148
  azure_endpoint=azure_endpoint,
@@ -69,6 +69,7 @@ class BenchmarkModelConfig:
69
69
  sampling: False for models that take no temperature (Claude Haiku 5.5); the call leaves it out
70
70
  reasoning: Reasoning level ("none" turns it off; see ``model_registry.reasoning_settings``)
71
71
  temperature: The model's own temperature (0 to 2), over LLM_TEMPERATURE; unused when sampling is off
72
+ context: The model's context size in tokens, when known (a local server's window); a call that fills it is warned
72
73
  """
73
74
 
74
75
  name: str
@@ -80,6 +81,7 @@ class BenchmarkModelConfig:
80
81
  sampling: bool = True
81
82
  reasoning: str | None = None
82
83
  temperature: float | None = None
84
+ context: int | None = None
83
85
 
84
86
  def __post_init__(self):
85
87
  """Validate provider and reasoning level."""
@@ -97,6 +99,8 @@ class BenchmarkModelConfig:
97
99
  reasoning_settings(self.provider, self.reasoning)
98
100
  if self.temperature is not None and not 0.0 <= self.temperature <= 2.0:
99
101
  raise ValueError(f"temperature must be between 0 and 2, got {self.temperature}")
102
+ if self.context is not None and self.context < 1:
103
+ raise ValueError(f"context must be a positive number of tokens, got {self.context}")
100
104
 
101
105
  def get_api_key(self) -> str | None:
102
106
  """Get API key from direct value or environment variable."""
@@ -143,7 +147,7 @@ class BaseResponse(BaseModel):
143
147
  class LiveResponse(BaseResponse):
144
148
  """Response from a live API call."""
145
149
 
146
- response_type: Literal[ResponseType.LIVE] = ResponseType.LIVE
150
+ response_type: Literal[ResponseType.LIVE] = ResponseType.LIVE # ty: ignore[invalid-mutable-override]: the discriminator of a pydantic union
147
151
  content: str
148
152
  usage: dict[str, Any] | None = None
149
153
  finish_reason: str | None = None
@@ -152,7 +156,7 @@ class LiveResponse(BaseResponse):
152
156
  class CachedResponse(BaseResponse):
153
157
  """Response retrieved from cache."""
154
158
 
155
- response_type: Literal[ResponseType.CACHED] = ResponseType.CACHED
159
+ response_type: Literal[ResponseType.CACHED] = ResponseType.CACHED # ty: ignore[invalid-mutable-override]: the discriminator of a pydantic union
156
160
  content: str
157
161
  cache_key: str
158
162
  cached_at: str
@@ -161,7 +165,7 @@ class CachedResponse(BaseResponse):
161
165
  class FailedResponse(BaseResponse):
162
166
  """Response when API call fails."""
163
167
 
164
- response_type: Literal[ResponseType.FAILED] = ResponseType.FAILED
168
+ response_type: Literal[ResponseType.FAILED] = ResponseType.FAILED # ty: ignore[invalid-mutable-override]: the discriminator of a pydantic union
165
169
  error: str
166
170
  error_type: str
167
171
 
@@ -9,7 +9,7 @@ wrapper exceptions.
9
9
  from __future__ import annotations
10
10
 
11
11
  import httpx2
12
- from pydantic_ai.exceptions import ModelHTTPError
12
+ from pydantic_ai.exceptions import IncompleteToolCall, ModelHTTPError
13
13
 
14
14
  from deriva.common.exceptions import (
15
15
  CircuitOpenError,
@@ -72,6 +72,10 @@ def classify_exception(
72
72
  if isinstance(exception, CircuitOpenError):
73
73
  return ("permanent", None)
74
74
 
75
+ # An answer cut at the output limit is cut again for the same prompt; the caller can split it instead
76
+ if isinstance(exception, IncompleteToolCall):
77
+ return ("permanent", None)
78
+
75
79
  # An HTTP status error carries its own status code
76
80
  if isinstance(exception, ModelHTTPError):
77
81
  status_code = getattr(exception, "status_code", None)
@@ -20,8 +20,7 @@ class Translator:
20
20
 
21
21
  def __init__(self, package_dir: Path):
22
22
  self.pieces = sentencepiece.SentencePieceProcessor(model_file=str(package_dir / "sentencepiece.model"))
23
- # ctranslate2 imports its compiled classes inside a try block, which ty reads as possibly missing
24
- self.model = ctranslate2.Translator(str(package_dir / "model"), device="cpu", inter_threads=4, intra_threads=1) # ty: ignore[possibly-missing-attribute]
23
+ self.model = ctranslate2.Translator(str(package_dir / "model"), device="cpu", inter_threads=4, intra_threads=1)
25
24
 
26
25
  def translate(self, texts: list[str], settings: Settings) -> dict[str, str]:
27
26
  """Each text on its own (batch size 1), so a translation never depends on the other texts."""