tai-sql 0.8.0__tar.gz → 0.8.2__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 (229) hide show
  1. {tai_sql-0.8.0 → tai_sql-0.8.2}/PKG-INFO +1 -1
  2. {tai_sql-0.8.0 → tai_sql-0.8.2}/pyproject.toml +1 -1
  3. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_schema/schemas/chatbot.py +247 -133
  4. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/domain/dtos.py.jinja2 +19 -1
  5. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/macros/write.jinja2 +23 -0
  6. {tai_sql-0.8.0 → tai_sql-0.8.2}/LICENSE +0 -0
  7. {tai_sql-0.8.0 → tai_sql-0.8.2}/README.md +0 -0
  8. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/__init__.py +0 -0
  9. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/__init__.py +0 -0
  10. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_backup/__init__.py +0 -0
  11. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_backup/main.py +0 -0
  12. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_deploy/__init__.py +0 -0
  13. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_deploy/config.py +0 -0
  14. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_deploy/local.py +0 -0
  15. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_deploy/main.py +0 -0
  16. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_deploy/workflow.py +0 -0
  17. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_feed/__init__.py +0 -0
  18. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_feed/main.py +0 -0
  19. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_generate/__init__.py +0 -0
  20. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_generate/funcs.py +0 -0
  21. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_generate/main.py +0 -0
  22. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_hash_migrate/__init__.py +0 -0
  23. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_hash_migrate/main.py +0 -0
  24. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_info/__init__.py +0 -0
  25. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_info/main.py +0 -0
  26. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_info/status.py +0 -0
  27. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_init/__init__.py +0 -0
  28. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_init/main.py +0 -0
  29. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_init/model.py +0 -0
  30. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_install/__init__.py +0 -0
  31. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_install/main.py +0 -0
  32. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_ping/__init__.py +0 -0
  33. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_ping/main.py +0 -0
  34. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_pull/__init__.py +0 -0
  35. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_pull/funcs.py +0 -0
  36. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_pull/main.py +0 -0
  37. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_restore/__init__.py +0 -0
  38. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_restore/main.py +0 -0
  39. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_rules/__init__.py +0 -0
  40. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_rules/main.py +0 -0
  41. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_schema/__init__.py +0 -0
  42. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_schema/model.py +0 -0
  43. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_schema/new.py +0 -0
  44. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_schema/schemas/__init__.py +0 -0
  45. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_schema/schemas/auth.py +0 -0
  46. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_schema/schemas/base.py +0 -0
  47. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_schema/schemas/default.py +0 -0
  48. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/cmd_schema/set_default.py +0 -0
  49. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/prompts.py +0 -0
  50. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/utils/__init__.py +0 -0
  51. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/utils/connectivity.py +0 -0
  52. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/utils/create.py +0 -0
  53. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/utils/github/__init__.py +0 -0
  54. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/utils/github/auth.py +0 -0
  55. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/utils/github/client.py +0 -0
  56. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/commands/utils/github/models.py +0 -0
  57. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/errors.py +0 -0
  58. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/main.py +0 -0
  59. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/preamble.py +0 -0
  60. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/services/__init__.py +0 -0
  61. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/cli/services/backup.py +0 -0
  62. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/connection/README.md +0 -0
  63. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/connection/__init__.py +0 -0
  64. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/connection/provider.py +0 -0
  65. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/connection/recipe.py +0 -0
  66. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/connection/session.py +0 -0
  67. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/connection/sources.py +0 -0
  68. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/crypto.py +0 -0
  69. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/drivers/__init__.py +0 -0
  70. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/drivers/base.py +0 -0
  71. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/drivers/bigquery.py +0 -0
  72. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/drivers/capabilities.py +0 -0
  73. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/drivers/mysql.py +0 -0
  74. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/drivers/postgresql.py +0 -0
  75. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/drivers/sqlserver.py +0 -0
  76. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/errors/README.md +0 -0
  77. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/errors/__init__.py +0 -0
  78. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/errors/collector.py +0 -0
  79. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/errors/render.py +0 -0
  80. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/errors/types.py +0 -0
  81. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/feed/README.md +0 -0
  82. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/feed/__init__.py +0 -0
  83. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/feed/collect.py +0 -0
  84. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/feed/executor.py +0 -0
  85. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/feed/feeder.py +0 -0
  86. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/feed/plan.py +0 -0
  87. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/feed/presenter.py +0 -0
  88. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/feed/records.py +0 -0
  89. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/feed/transform.py +0 -0
  90. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/__init__.py +0 -0
  91. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/base.py +0 -0
  92. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/diagram/README.md +0 -0
  93. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/diagram/__init__.py +0 -0
  94. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/diagram/assets/README.md +0 -0
  95. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/diagram/assets/cytoscape.min.js +0 -0
  96. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/diagram/builder.py +0 -0
  97. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/diagram/generator.py +0 -0
  98. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/diagram/graphviz_render.py +0 -0
  99. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/diagram/html_render.py +0 -0
  100. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/diagram/layout.py +0 -0
  101. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/diagram/model.py +0 -0
  102. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/diagram/templates/er.html.jinja2 +0 -0
  103. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/diagram/themes.py +0 -0
  104. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/__init__.py +0 -0
  105. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/calculated.py +0 -0
  106. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/context.py +0 -0
  107. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/base.py.jinja2 +0 -0
  108. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/domain/__init__.py.jinja2 +0 -0
  109. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/domain/dao.py.jinja2 +0 -0
  110. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/domain/model.py.jinja2 +0 -0
  111. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/facade.py.jinja2 +0 -0
  112. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/macros/aggregation.jinja2 +0 -0
  113. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/macros/dataframe.jinja2 +0 -0
  114. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/macros/filters.jinja2 +0 -0
  115. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/macros/hierarchy.jinja2 +0 -0
  116. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/macros/read.jinja2 +0 -0
  117. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/macros/triggers.jinja2 +0 -0
  118. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/macros/upsert.jinja2 +0 -0
  119. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/macros/vector.jinja2 +0 -0
  120. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/session.py.jinja2 +0 -0
  121. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/shared/__init__.py.jinja2 +0 -0
  122. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/shared/aggregation.py.jinja2 +0 -0
  123. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/shared/binary.py.jinja2 +0 -0
  124. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/shared/context.py.jinja2 +0 -0
  125. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/shared/utils/__init__.py.jinja2 +0 -0
  126. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/shared/utils/encoders.py.jinja2 +0 -0
  127. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/shared/utils/funcs.py.jinja2 +0 -0
  128. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/shared/utils/hierarchy.py.jinja2 +0 -0
  129. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/shared/utils/rls.py.jinja2 +0 -0
  130. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/templates/shared/vector.py.jinja2 +0 -0
  131. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/triggers/__init__.py +0 -0
  132. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/triggers/analyzer.py +0 -0
  133. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/triggers/renderer.py +0 -0
  134. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/triggers/scopes.py +0 -0
  135. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/triggers/steps.py +0 -0
  136. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/python_client/triggers/transformer.py +0 -0
  137. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/rules/__init__.py +0 -0
  138. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/generators/rules/templates/schema.md.jinja2 +0 -0
  139. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/introspect/README.md +0 -0
  140. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/introspect/__init__.py +0 -0
  141. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/introspect/blueprint.py +0 -0
  142. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/introspect/build.py +0 -0
  143. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/introspect/defaults.py +0 -0
  144. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/introspect/naming.py +0 -0
  145. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/introspect/reader.py +0 -0
  146. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/introspect/templates/schema.py.jinja2 +0 -0
  147. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/introspect/types.py +0 -0
  148. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/introspect/writer.py +0 -0
  149. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/README.md +0 -0
  150. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/__init__.py +0 -0
  151. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/analysis/README.md +0 -0
  152. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/analysis/__init__.py +0 -0
  153. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/analysis/calculated.py +0 -0
  154. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/analysis/collector.py +0 -0
  155. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/analysis/column_analyzer.py +0 -0
  156. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/analysis/constraints.py +0 -0
  157. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/analysis/hints.py +0 -0
  158. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/analysis/relation_analyzer.py +0 -0
  159. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/analysis/schema_analyzer.py +0 -0
  160. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/declarative/README.md +0 -0
  161. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/declarative/__init__.py +0 -0
  162. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/declarative/annotations.py +0 -0
  163. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/declarative/base.py +0 -0
  164. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/declarative/calculated.py +0 -0
  165. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/declarative/columns.py +0 -0
  166. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/declarative/constraints.py +0 -0
  167. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/declarative/datasource.py +0 -0
  168. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/declarative/descriptors.py +0 -0
  169. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/declarative/encoders.py +0 -0
  170. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/declarative/enumerations.py +0 -0
  171. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/declarative/relations.py +0 -0
  172. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/declarative/table.py +0 -0
  173. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/declarative/trigger_api.py +0 -0
  174. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/declarative/triggers.py +0 -0
  175. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/declarative/view.py +0 -0
  176. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/mapping/README.md +0 -0
  177. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/mapping/__init__.py +0 -0
  178. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/mapping/sqlalchemy_mapper.py +0 -0
  179. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/model/README.md +0 -0
  180. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/model/__init__.py +0 -0
  181. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/model/column.py +0 -0
  182. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/model/constraints.py +0 -0
  183. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/model/entity.py +0 -0
  184. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/model/enumeration.py +0 -0
  185. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/model/relation.py +0 -0
  186. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/model/schema.py +0 -0
  187. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/model/trigger.py +0 -0
  188. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/naming.py +0 -0
  189. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/ordering.py +0 -0
  190. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/registry.py +0 -0
  191. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/orm/types.py +0 -0
  192. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/project/README.md +0 -0
  193. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/project/__init__.py +0 -0
  194. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/project/config.py +0 -0
  195. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/project/context.py +0 -0
  196. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/project/manager.py +0 -0
  197. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/project/paths.py +0 -0
  198. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/rules/README.md +0 -0
  199. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/rules/__init__.py +0 -0
  200. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/rules/catalog.py +0 -0
  201. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/rules/installer.py +0 -0
  202. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/rules/meta.py +0 -0
  203. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/rules/service.py +0 -0
  204. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/rules/staleness.py +0 -0
  205. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/rules/static/comandos.md +0 -0
  206. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/rules/static/errores.md +0 -0
  207. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/rules/static/modelo.md +0 -0
  208. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/schema.py +0 -0
  209. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/README.md +0 -0
  210. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/__init__.py +0 -0
  211. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/diff/__init__.py +0 -0
  212. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/diff/constraints.py +0 -0
  213. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/diff/indexes.py +0 -0
  214. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/diff/tables.py +0 -0
  215. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/diff/views.py +0 -0
  216. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/executor.py +0 -0
  217. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/ownership.py +0 -0
  218. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/plan/__init__.py +0 -0
  219. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/plan/planner.py +0 -0
  220. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/plan/statements.py +0 -0
  221. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/presenter.py +0 -0
  222. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/report.py +0 -0
  223. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/safety.py +0 -0
  224. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/state/__init__.py +0 -0
  225. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/state/local.py +0 -0
  226. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/state/model.py +0 -0
  227. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/state/normalize.py +0 -0
  228. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/state/remote.py +0 -0
  229. {tai_sql-0.8.0 → tai_sql-0.8.2}/tai_sql/sync/synchronizer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tai-sql
3
- Version: 0.8.0
3
+ Version: 0.8.2
4
4
  Summary: SQL database management and code generation tool
5
5
  License-Expression: LicenseRef-Proprietary
6
6
  License-File: LICENSE
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tai-sql"
3
- version = "0.8.0"
3
+ version = "0.8.2"
4
4
  description = "SQL database management and code generation tool"
5
5
  license = "LicenseRef-Proprietary"
6
6
  license-files = ["LICENSE"]
@@ -6,7 +6,7 @@ from .base import SchemaTemplate
6
6
 
7
7
 
8
8
  class ChatbotSchema(SchemaTemplate):
9
- """Plantilla chatbot: Chat, Mensaje, TokenUsage, ToolCall, Modelo, Tarifa, Bolsa,
9
+ """Plantilla chatbot: Chat, Mensaje, TokenUsage, TokenUsageParte, ToolCall, Sugerencia, Modelo, Tarifa, Bolsa,
10
10
  LimitePresupuesto y vistas"""
11
11
 
12
12
  def get_content(self) -> str:
@@ -105,6 +105,7 @@ class ChatbotSchema(SchemaTemplate):
105
105
 
106
106
  token_usage: onetomany[TokenUsage] # Relación one-to-many con la tabla TokenUsage
107
107
  tool_calls: onetomany[ToolCall] # Relación one-to-many con la tabla ToolCall
108
+ sugerencias: onetomany[Sugerencia] # Lo que se sugiere preguntar tras esta respuesta
108
109
 
109
110
  chat: manytoone[Chat] = relation(fields=['chat_id'], references=['id'], backref='messages') # Relación many-to-one con Chat
110
111
 
@@ -131,8 +132,19 @@ class ChatbotSchema(SchemaTemplate):
131
132
  tarifa_id: col[bigint | None] = column(index=True, description='Tarifa con la que se valora')
132
133
  bolsa_id: col[bigint | None] = column(index=True, description='Bolsa a la que se carga; vacío si no hay bolsas')
133
134
 
135
+ # De dónde sale y lo exacto que dio el proveedor; de qué se compone va en sus partes
136
+ # (token_usage_parte). Lo que se cobra sigue siendo prompt_tokens y completion_tokens:
137
+ # las partes los reparten, y suman lo mismo. Vacíos en los apuntes anteriores
138
+ origen: col[str | None] = column(index=True, description='De dónde sale: respuesta, subagente, titulo, sugerencias o resumen')
139
+ llamadas: col[int | None] = column(description='Cuántas llamadas al modelo hizo')
140
+ cache_read: col[int | None] = column(description='Tokens de la entrada leídos de la caché del proveedor, ya incluidos en prompt_tokens')
141
+ cache_write: col[int | None] = column(description='Tokens de la entrada escritos en la caché del proveedor, ya incluidos en prompt_tokens')
142
+ estimado: col[bool | None] = column(description='Si los tokens se estimaron por el texto porque el proveedor no dio el uso: entonces no tiene partes')
143
+ herramienta: col[str | None] = column(description='La herramienta que creó el sub-agente, si el origen es subagente')
144
+
134
145
  message: manytoone[Mensaje] = relation(fields=['message_id'], references=['id'], backref='token_usage', onDelete='set null')
135
146
  chat: manytoone[Chat] = relation(fields=['chat_id'], references=['id'], backref='token_usage', onDelete='set null')
147
+ partes: onetomany[TokenUsageParte] # De qué se compone: las partes de su entrada y su salida
136
148
 
137
149
 
138
150
  class ToolCall(Table):
@@ -151,6 +163,34 @@ class ChatbotSchema(SchemaTemplate):
151
163
  message: manytoone[Mensaje] = relation(fields=['message_id'], references=['id'], backref='tool_calls') # Relación many-to-one con Mensaje
152
164
 
153
165
 
166
+ class TokenUsageParte(Table):
167
+ """Tabla con las partes de un apunte de consumo: de qué se compone su entrada y su salida"""
168
+ __tablename__ = "token_usage_parte"
169
+
170
+ id: col[bigint] = column(primary_key=True, autoincrement=True)
171
+ tipo: col[str] = column(description="'entrada' o 'salida'")
172
+ parte: col[str] = column(description='sistema, definiciones, contexto, contexto_herramientas, mensaje, ronda o peticion en la entrada; texto, llamadas_herramienta o razonamiento en la salida')
173
+ tokens: col[int] = column(description='Los tokens de esta parte: las de un tipo suman los del apunte')
174
+ token_usage_id: col[bigint] = column(index=True, description='El apunte del que es parte')
175
+
176
+ # `apunte` y no `token_usage`: los triggers en cascada comparan las relaciones por su
177
+ # nombre, sin la ruta, y con el mismo nombre que Mensaje.token_usage, crear un mensaje
178
+ # con sus apuntes y sus partes subía y bajaba entre apunte y parte sin fin
179
+ apunte: manytoone[TokenUsage] = relation(fields=['token_usage_id'], references=['id'], backref='partes', onDelete='cascade')
180
+
181
+
182
+ class Sugerencia(Table):
183
+ """Tabla con lo que se sugiere preguntar tras una respuesta del asistente"""
184
+ __tablename__ = "sugerencia"
185
+
186
+ id: col[bigint] = column(primary_key=True, autoincrement=True)
187
+ texto: col[str] = column(description='La sugerencia, escrita como la diría el usuario')
188
+ posicion: col[int] = column(description='Su orden entre las de la misma respuesta, desde 0')
189
+ message_id: col[bigint] = column(index=True, description='La respuesta tras la que se sugiere')
190
+
191
+ message: manytoone[Mensaje] = relation(fields=['message_id'], references=['id'], backref='sugerencias', onDelete='cascade')
192
+
193
+
154
194
  class Modelo(Table):
155
195
  """Tabla que almacena los modelos con los que puede responder el agente"""
156
196
  __tablename__ = "modelo"
@@ -183,6 +223,11 @@ class ChatbotSchema(SchemaTemplate):
183
223
  modelo_id: col[bigint] = column(description='Modelo al que pone precio')
184
224
  precio_input_eur_millon: col[float] = column(default=0.0, description='Euros por millón de tokens de entrada, al precio del proveedor')
185
225
  precio_output_eur_millon: col[float] = column(default=0.0, description='Euros por millón de tokens de salida, al precio del proveedor')
226
+ # La entrada que sale de la caché del proveedor («cached input») y la que se escribe en ella
227
+ # («cache writes»: solo OpenAI y Azure desde GPT-5.6) tienen su precio. Vacío es al precio
228
+ # de entrada, que es lo de una tarifa sin él
229
+ precio_cache_input_eur_millon: col[float | None] = column(description='Cache input: euros por millón de tokens de entrada leídos de la caché, al precio del proveedor; vacío, al de entrada')
230
+ precio_cache_write_eur_millon: col[float | None] = column(description='Cache write: euros por millón de tokens de entrada escritos en la caché, al precio del proveedor; vacío, al de entrada')
186
231
  margen: col[float] = column(default=1.0, description='Multiplicador comercial sobre el precio del proveedor')
187
232
  desde: col[date] = column(description='Primer día en que rige')
188
233
  # Una tarifa no se edita ni se borra: se da de alta la siguiente con su `desde` y la
@@ -220,22 +265,6 @@ class ChatbotSchema(SchemaTemplate):
220
265
 
221
266
  # Definición de vistas
222
267
 
223
- class UserStats(View):
224
- """Vista que muestra estadísticas de usuarios y sus chats"""
225
- __tablename__ = "user_stats"
226
- __query__ = query('user_stats.sql') # Esto es necesario para usar tai-sql push
227
-
228
- username: col[str]
229
- total_chats: col[int]
230
- active_chats: col[int]
231
- total_messages: col[int]
232
- last_activity: col[datetime | None]
233
-
234
- most_used_model: col[str | None]
235
- most_used_model_total_tokens: col[int]
236
- most_used_model_active_days: col[int]
237
-
238
-
239
268
  class ConsumoDiario(View):
240
269
  """Vista que muestra el consumo por usuario, día y modelo"""
241
270
  __tablename__ = "consumo_diario"
@@ -253,20 +282,6 @@ class ChatbotSchema(SchemaTemplate):
253
282
  message_count: col[int]
254
283
 
255
284
 
256
- class ChatActivity(View):
257
- """Vista que muestra la actividad reciente de chats"""
258
- __tablename__ = "chat_activity"
259
- __query__ = query('chat_activity.sql')
260
-
261
- chat_id: col[str]
262
- chat_title: col[str]
263
- username: col[str]
264
- message_count: col[int]
265
- last_message_timestamp: col[datetime]
266
- total_tokens_consumed: col[int]
267
- is_active: col[bool]
268
-
269
-
270
285
  class ConsumoMensual(View):
271
286
  """Vista que muestra el consumo y el coste por usuario, mes y modelo"""
272
287
  __tablename__ = "consumo_mensual"
@@ -300,6 +315,80 @@ class ChatbotSchema(SchemaTemplate):
300
315
  total_tokens: col[int]
301
316
 
302
317
 
318
+ class ConsumoApunte(View):
319
+ """Vista que muestra cada apunte de consumo con su modelo y su coste"""
320
+ __tablename__ = "consumo_apunte"
321
+ __query__ = query('consumo_apunte.sql')
322
+
323
+ token_usage_id: col[bigint]
324
+ timestamp: col[datetime]
325
+ username: col[str]
326
+ message_id: col[bigint | None]
327
+ chat_id: col[bigint | None]
328
+ origen: col[str]
329
+ llamadas: col[int | None]
330
+ cache_read: col[int | None]
331
+ cache_write: col[int | None]
332
+ estimado: col[bool | None]
333
+ herramienta: col[str | None]
334
+ # Las vueltas del agente en el mensaje del que cuelga
335
+ rondas: col[int | None]
336
+ tarifa_id: col[bigint | None]
337
+ # Del modelo de la tarifa: vacíos si el apunte no tiene
338
+ modelo: col[str | None]
339
+ provider: col[str | None]
340
+ model_name: col[str | None]
341
+ prompt_tokens: col[int]
342
+ completion_tokens: col[int]
343
+ total_tokens: col[int]
344
+ # Como en consumo_mensual: por fila, con el margen y redondeado. Vacíos sin tarifa
345
+ coste_entrada_eur: col[float | None]
346
+ coste_salida_eur: col[float | None]
347
+ coste_eur: col[float | None]
348
+ # Lo que la caché ahorró frente al precio de entrada (negativo si escribir costó más), y
349
+ # si la tarifa tiene precio para lo leído
350
+ ahorro_cache_eur: col[float | None]
351
+ precio_cache: col[bool]
352
+
353
+
354
+ class ConsumoParte(View):
355
+ """Vista que muestra cada parte de un apunte de consumo con su coste"""
356
+ __tablename__ = "consumo_parte"
357
+ __query__ = query('consumo_parte.sql')
358
+
359
+ parte_id: col[bigint]
360
+ token_usage_id: col[bigint]
361
+ # Del apunte, para filtrar las partes igual que los apuntes
362
+ timestamp: col[datetime]
363
+ username: col[str]
364
+ modelo: col[str | None]
365
+ # 'entrada' o 'salida'
366
+ tipo: col[str]
367
+ parte: col[str]
368
+ tokens: col[int]
369
+ coste_eur: col[float | None]
370
+
371
+
372
+ class Valoracion(View):
373
+ """Vista que muestra las respuestas valoradas con un comentario"""
374
+ __tablename__ = "valoracion"
375
+ __query__ = query('valoracion.sql')
376
+
377
+ message_id: col[bigint]
378
+ timestamp: col[datetime]
379
+ content: col[str]
380
+ # 'good' o 'bad'
381
+ feedback: col[str]
382
+ feedback_comment: col[str]
383
+ total_duration_ms: col[int | None]
384
+ model_name: col[str | None]
385
+ chat_id: col[bigint]
386
+ chat_title: col[str | None]
387
+ username: col[str]
388
+ # Todo lo que se apuntó con la respuesta: la suya y la de sus sub-agentes
389
+ total_tokens: col[int | None]
390
+
391
+
303
392
  class BolsaEstado(View):
304
393
  """Vista que muestra cuánto lleva gastado cada bolsa de consumo"""
305
394
  __tablename__ = "bolsa_estado"
@@ -316,22 +405,6 @@ class ChatbotSchema(SchemaTemplate):
316
405
  estado: col[str]
317
406
 
318
407
 
319
- class TarifaVigente(View):
320
- """Vista que muestra la tarifa que rige hoy para cada modelo, a precio de venta"""
321
- __tablename__ = "tarifa_vigente"
322
- __query__ = query('tarifa_vigente.sql')
323
-
324
- tarifa_id: col[bigint]
325
- modelo_id: col[bigint]
326
- alias: col[str]
327
- provider: col[str]
328
- model_name: col[str]
329
- precio_input_eur_millon: col[float]
330
- precio_output_eur_millon: col[float]
331
- desde: col[date]
332
- hasta: col[date | None]
333
-
334
-
335
408
  class TarifaHistorico(View):
336
409
  """Vista que muestra todas las tarifas de cada modelo, a precio de venta"""
337
410
  __tablename__ = "tarifa_historico"
@@ -344,8 +417,12 @@ class ChatbotSchema(SchemaTemplate):
344
417
  model_name: col[str]
345
418
  precio_input_eur_millon: col[float]
346
419
  precio_output_eur_millon: col[float]
420
+ # Vacíos si la tarifa no los tiene: lo de la caché va al precio de entrada
421
+ precio_cache_input_eur_millon: col[float | None]
422
+ precio_cache_write_eur_millon: col[float | None]
347
423
  desde: col[date]
348
424
  hasta: col[date | None]
425
+ # La que rige hoy: una por modelo como mucho
349
426
  vigente: col[bool]
350
427
 
351
428
 
@@ -389,7 +466,11 @@ class ChatbotSchema(SchemaTemplate):
389
466
  b.nombre,
390
467
  b.importe_eur,
391
468
  b.created_at,
469
+ -- La entrada, a precio de entrada, más lo que cambia la caché: lo leído de ella (cache input) y lo
470
+ -- escrito en ella (cache write), cada uno a su precio y, sin él, al de entrada (la diferencia es 0)
392
471
  COALESCE(SUM(ROUND(((tu.prompt_tokens * t.precio_input_eur_millon
472
+ + COALESCE(tu.cache_read, 0) * (COALESCE(t.precio_cache_input_eur_millon, t.precio_input_eur_millon) - t.precio_input_eur_millon)
473
+ + COALESCE(tu.cache_write, 0) * (COALESCE(t.precio_cache_write_eur_millon, t.precio_input_eur_millon) - t.precio_input_eur_millon)
393
474
  + tu.completion_tokens * t.precio_output_eur_millon)
394
475
  * t.margen / 1000000)::numeric, 8)), 0)::double precision as consumido_eur,
395
476
  MIN(tu.timestamp) as primer_consumo,
@@ -417,23 +498,6 @@ class ChatbotSchema(SchemaTemplate):
417
498
  ORDER BY g.id;
418
499
  ''').rstrip()
419
500
 
420
- chat_activity_sql = dedent('''\
421
- -- Vista para actividad de chats
422
- SELECT
423
- c.id as chat_id,
424
- c.title as chat_title,
425
- c.username,
426
- COUNT(m.id) as message_count,
427
- MAX(m.timestamp) as last_message_timestamp,
428
- COALESCE(SUM(tu.total_tokens), 0) as total_tokens_consumed,
429
- c.is_active
430
- FROM chat c
431
- LEFT JOIN mensaje m ON c.id = m.chat_id
432
- LEFT JOIN token_usage tu ON m.id = tu.message_id
433
- GROUP BY c.id, c.title, c.username, c.is_active
434
- ORDER BY last_message_timestamp DESC NULLS LAST;
435
- ''').rstrip()
436
-
437
501
  consumo_diario_sql = dedent('''\
438
502
  -- Vista para el consumo por usuario, día y modelo. Los tokens y el importe son todos los
439
503
  -- apuntes, tengan mensaje o no; los mensajes, los que envió el usuario
@@ -446,7 +510,11 @@ class ChatbotSchema(SchemaTemplate):
446
510
  SUM(tu.prompt_tokens) as total_prompt_tokens,
447
511
  SUM(tu.completion_tokens) as total_completion_tokens,
448
512
  SUM(tu.total_tokens) as total_tokens,
513
+ -- La entrada, a precio de entrada, más lo que cambia la caché: lo leído de ella (cache input) y lo
514
+ -- escrito en ella (cache write), cada uno a su precio y, sin él, al de entrada (la diferencia es 0)
449
515
  SUM(ROUND(((tu.prompt_tokens * t.precio_input_eur_millon
516
+ + COALESCE(tu.cache_read, 0) * (COALESCE(t.precio_cache_input_eur_millon, t.precio_input_eur_millon) - t.precio_input_eur_millon)
517
+ + COALESCE(tu.cache_write, 0) * (COALESCE(t.precio_cache_write_eur_millon, t.precio_input_eur_millon) - t.precio_input_eur_millon)
450
518
  + tu.completion_tokens * t.precio_output_eur_millon)
451
519
  * t.margen / 1000000)::numeric, 8))::double precision as total_cost_eur,
452
520
  COUNT(DISTINCT COALESCE(tu.chat_id, me.chat_id)) as chat_count
@@ -512,7 +580,11 @@ class ChatbotSchema(SchemaTemplate):
512
580
  SUM(tu.prompt_tokens) as total_prompt_tokens,
513
581
  SUM(tu.completion_tokens) as total_completion_tokens,
514
582
  SUM(tu.total_tokens) as total_tokens,
583
+ -- La entrada, a precio de entrada, más lo que cambia la caché: lo leído de ella (cache input) y lo
584
+ -- escrito en ella (cache write), cada uno a su precio y, sin él, al de entrada (la diferencia es 0)
515
585
  SUM(ROUND(((tu.prompt_tokens * t.precio_input_eur_millon
586
+ + COALESCE(tu.cache_read, 0) * (COALESCE(t.precio_cache_input_eur_millon, t.precio_input_eur_millon) - t.precio_input_eur_millon)
587
+ + COALESCE(tu.cache_write, 0) * (COALESCE(t.precio_cache_write_eur_millon, t.precio_input_eur_millon) - t.precio_input_eur_millon)
516
588
  + tu.completion_tokens * t.precio_output_eur_millon)
517
589
  * t.margen / 1000000)::numeric, 8))::double precision as coste_eur
518
590
  FROM token_usage tu
@@ -561,6 +633,112 @@ class ChatbotSchema(SchemaTemplate):
561
633
  ORDER BY mes DESC, username, total_tokens DESC;
562
634
  ''').rstrip()
563
635
 
636
+ consumo_apunte_sql = dedent('''\
637
+ -- Vista con cada apunte de consumo: de dónde sale, con qué modelo y lo exacto que dio el
638
+ -- proveedor y las vueltas del agente, con su coste calculado como en consumo_mensual (por fila, con el margen y
639
+ -- redondeado a 8 decimales). Un apunte sin tarifa sale con el modelo y el coste vacíos.
640
+ -- Un apunte sin origen es 'respuesta' si cuelga de un mensaje, y si no 'sin_clasificar'
641
+ SELECT
642
+ tu.id as token_usage_id,
643
+ tu.timestamp,
644
+ tu.username,
645
+ tu.message_id,
646
+ tu.chat_id,
647
+ COALESCE(tu.origen, CASE WHEN tu.message_id IS NOT NULL THEN 'respuesta' ELSE 'sin_clasificar' END) as origen,
648
+ tu.llamadas,
649
+ tu.cache_read,
650
+ tu.cache_write,
651
+ tu.estimado,
652
+ tu.herramienta,
653
+ ms.agent_iterations as rondas,
654
+ tu.tarifa_id,
655
+ m.alias as modelo,
656
+ m.provider,
657
+ m.model_name,
658
+ tu.prompt_tokens,
659
+ tu.completion_tokens,
660
+ tu.total_tokens,
661
+ -- La entrada, a precio de entrada, más lo que cambia la caché: lo leído de ella (cache input) y lo
662
+ -- escrito en ella (cache write), cada uno a su precio y, sin él, al de entrada (la diferencia es 0)
663
+ ROUND(((tu.prompt_tokens * t.precio_input_eur_millon
664
+ + COALESCE(tu.cache_read, 0) * (COALESCE(t.precio_cache_input_eur_millon, t.precio_input_eur_millon) - t.precio_input_eur_millon)
665
+ + COALESCE(tu.cache_write, 0) * (COALESCE(t.precio_cache_write_eur_millon, t.precio_input_eur_millon) - t.precio_input_eur_millon))
666
+ * t.margen / 1000000)::numeric, 8)::double precision as coste_entrada_eur,
667
+ ROUND((tu.completion_tokens * t.precio_output_eur_millon
668
+ * t.margen / 1000000)::numeric, 8)::double precision as coste_salida_eur,
669
+ ROUND(((tu.prompt_tokens * t.precio_input_eur_millon
670
+ + COALESCE(tu.cache_read, 0) * (COALESCE(t.precio_cache_input_eur_millon, t.precio_input_eur_millon) - t.precio_input_eur_millon)
671
+ + COALESCE(tu.cache_write, 0) * (COALESCE(t.precio_cache_write_eur_millon, t.precio_input_eur_millon) - t.precio_input_eur_millon)
672
+ + tu.completion_tokens * t.precio_output_eur_millon)
673
+ * t.margen / 1000000)::numeric, 8)::double precision as coste_eur,
674
+ -- lo que la caché ahorró frente a cobrar la entrada entera a precio de entrada (negativo
675
+ -- si escribir en ella costó más), y si la tarifa tiene precio de cache input
676
+ ROUND((-(COALESCE(tu.cache_read, 0) * (COALESCE(t.precio_cache_input_eur_millon, t.precio_input_eur_millon) - t.precio_input_eur_millon)
677
+ + COALESCE(tu.cache_write, 0) * (COALESCE(t.precio_cache_write_eur_millon, t.precio_input_eur_millon) - t.precio_input_eur_millon))
678
+ * t.margen / 1000000)::numeric, 8)::double precision as ahorro_cache_eur,
679
+ (t.precio_cache_input_eur_millon IS NOT NULL) as precio_cache
680
+ FROM token_usage tu
681
+ LEFT JOIN tarifa t ON t.id = tu.tarifa_id
682
+ LEFT JOIN modelo m ON m.id = t.modelo_id
683
+ LEFT JOIN mensaje ms ON ms.id = tu.message_id
684
+ ORDER BY tu.timestamp, tu.id;
685
+ ''').rstrip()
686
+
687
+ consumo_parte_sql = dedent('''\
688
+ -- Vista con cada parte de un apunte de consumo (token_usage_parte) y su coste, como en
689
+ -- consumo_apunte: la salida a su precio, y la entrada al precio medio de la de su apunte, que
690
+ -- reparte lo que cambia la caché (no se sabe qué parte salió de ella). Lleva la fecha, el
691
+ -- usuario y el modelo del apunte, para filtrar las
692
+ -- partes igual que los apuntes. Un apunte sin partes no sale: las suyas son el apunte
693
+ -- entero, que está en consumo_apunte
694
+ SELECT
695
+ p.id as parte_id,
696
+ p.token_usage_id,
697
+ tu.timestamp,
698
+ tu.username,
699
+ m.alias as modelo,
700
+ p.tipo,
701
+ p.parte,
702
+ p.tokens,
703
+ ROUND((p.tokens * CASE WHEN p.tipo = 'entrada'
704
+ THEN t.precio_input_eur_millon
705
+ + COALESCE((COALESCE(tu.cache_read, 0) * (COALESCE(t.precio_cache_input_eur_millon, t.precio_input_eur_millon) - t.precio_input_eur_millon)
706
+ + COALESCE(tu.cache_write, 0) * (COALESCE(t.precio_cache_write_eur_millon, t.precio_input_eur_millon) - t.precio_input_eur_millon))
707
+ / NULLIF(tu.prompt_tokens, 0), 0)
708
+ ELSE t.precio_output_eur_millon END
709
+ * t.margen / 1000000)::numeric, 8)::double precision as coste_eur
710
+ FROM token_usage_parte p
711
+ JOIN token_usage tu ON tu.id = p.token_usage_id
712
+ LEFT JOIN tarifa t ON t.id = tu.tarifa_id
713
+ LEFT JOIN modelo m ON m.id = t.modelo_id
714
+ WHERE p.tokens <> 0
715
+ ORDER BY p.id;
716
+ ''').rstrip()
717
+
718
+ valoracion_sql = dedent('''\
719
+ -- Vista con las respuestas que el usuario valoró con un comentario, con su conversación
720
+ -- y lo que se apuntó con ellas. Solo las que tienen comentario: la columna feedback nace
721
+ -- 'good', así que una valoración sin texto no se distingue de un mensaje sin valorar
722
+ SELECT
723
+ m.id as message_id,
724
+ m.timestamp,
725
+ m.content,
726
+ m.feedback::text as feedback,
727
+ m.feedback_comment,
728
+ m.total_duration_ms,
729
+ m.model_name,
730
+ c.id as chat_id,
731
+ c.title as chat_title,
732
+ c.username,
733
+ (SELECT SUM(tu.total_tokens) FROM token_usage tu WHERE tu.message_id = m.id) as total_tokens
734
+ FROM mensaje m
735
+ JOIN chat c ON c.id = m.chat_id
736
+ WHERE m.role = 'asistente'
737
+ AND m.feedback_comment IS NOT NULL
738
+ AND trim(m.feedback_comment) <> ''
739
+ ORDER BY m.timestamp DESC;
740
+ ''').rstrip()
741
+
564
742
  modelo_disponible_sql = dedent('''\
565
743
  -- Vista con el catálogo de modelos; el endpoint y la variable de la clave no salen de aquí
566
744
  WITH ventana AS (
@@ -613,6 +791,8 @@ class ChatbotSchema(SchemaTemplate):
613
791
  m.model_name,
614
792
  (v.precio_input_eur_millon * v.margen)::double precision as precio_input_eur_millon,
615
793
  (v.precio_output_eur_millon * v.margen)::double precision as precio_output_eur_millon,
794
+ (v.precio_cache_input_eur_millon * v.margen)::double precision as precio_cache_input_eur_millon,
795
+ (v.precio_cache_write_eur_millon * v.margen)::double precision as precio_cache_write_eur_millon,
616
796
  v.desde,
617
797
  v.hasta_efectivo as hasta,
618
798
  (v.desde <= CURRENT_DATE
@@ -622,36 +802,6 @@ class ChatbotSchema(SchemaTemplate):
622
802
  ORDER BY m.alias, v.desde DESC;
623
803
  ''').rstrip()
624
804
 
625
- tarifa_vigente_sql = dedent('''\
626
- -- Vista con la tarifa que rige hoy para cada modelo, ya a precio de venta: el precio del
627
- -- proveedor y el margen no salen de la tabla. La ventana se deriva de la tarifa siguiente,
628
- -- así que dar de alta una tarifa nueva no obliga a cerrar la anterior
629
- WITH ventana AS (
630
- SELECT
631
- t.*,
632
- COALESCE(
633
- t.hasta,
634
- LEAD(t.desde) OVER (PARTITION BY t.modelo_id ORDER BY t.desde) - 1
635
- ) as hasta_efectivo
636
- FROM tarifa t
637
- )
638
- SELECT
639
- v.id as tarifa_id,
640
- v.modelo_id,
641
- m.alias,
642
- m.provider,
643
- m.model_name,
644
- (v.precio_input_eur_millon * v.margen)::double precision as precio_input_eur_millon,
645
- (v.precio_output_eur_millon * v.margen)::double precision as precio_output_eur_millon,
646
- v.desde,
647
- v.hasta_efectivo as hasta
648
- FROM ventana v
649
- JOIN modelo m ON m.id = v.modelo_id
650
- WHERE v.desde <= CURRENT_DATE
651
- AND (v.hasta_efectivo IS NULL OR v.hasta_efectivo >= CURRENT_DATE)
652
- ORDER BY m.alias;
653
- ''').rstrip()
654
-
655
805
  tokens_mensaje_sql = dedent('''\
656
806
  -- Vista con los tokens de cada mensaje; no lleva importes a propósito: es la única que
657
807
  -- puede leer cualquier usuario
@@ -670,42 +820,6 @@ class ChatbotSchema(SchemaTemplate):
670
820
  GROUP BY tu.message_id, m.chat_id, c.username, m.model_name, m.provider;
671
821
  ''').rstrip()
672
822
 
673
- user_stats_sql = dedent('''\
674
- -- Vista para estadísticas de usuarios
675
- WITH top_model AS (
676
- SELECT DISTINCT ON (c.username)
677
- c.username,
678
- m.model_name,
679
- SUM(tu.total_tokens) AS total_tokens,
680
- COUNT(DISTINCT DATE(tu.timestamp)) AS active_days
681
- FROM chat c
682
- JOIN mensaje m ON c.id = m.chat_id
683
- JOIN token_usage tu ON m.id = tu.message_id
684
- GROUP BY c.username, m.model_name
685
- ORDER BY c.username, SUM(tu.total_tokens) DESC
686
- )
687
-
688
- SELECT
689
- c.username,
690
- COUNT(DISTINCT c.id) AS total_chats,
691
- COUNT(DISTINCT CASE WHEN c.is_active THEN c.id END) AS active_chats,
692
- COUNT(DISTINCT m.id) AS total_messages,
693
- MAX(m.timestamp) AS last_activity,
694
-
695
- tm.model_name AS most_used_model,
696
- tm.total_tokens AS most_used_model_total_tokens,
697
- tm.active_days AS most_used_model_active_days
698
-
699
- FROM chat c
700
- LEFT JOIN mensaje m ON c.id = m.chat_id
701
- LEFT JOIN top_model tm ON c.username = tm.username
702
- GROUP BY
703
- c.username,
704
- tm.model_name,
705
- tm.total_tokens,
706
- tm.active_days;
707
- ''').rstrip()
708
-
709
823
  usuario_consumo_sql = dedent('''\
710
824
  -- Vista con los usuarios a los que se mide el consumo. Es la que leen sus pantallas:
711
825
  -- /rbac/{realm}/user pide administración del realm y la contraseña no sale de aquí
@@ -723,12 +837,12 @@ class ChatbotSchema(SchemaTemplate):
723
837
  ''').rstrip()
724
838
 
725
839
  (views_dir / 'bolsa_estado.sql').write_text(bolsa_estado_sql, encoding='utf-8')
726
- (views_dir / 'chat_activity.sql').write_text(chat_activity_sql, encoding='utf-8')
840
+ (views_dir / 'consumo_apunte.sql').write_text(consumo_apunte_sql, encoding='utf-8')
841
+ (views_dir / 'consumo_parte.sql').write_text(consumo_parte_sql, encoding='utf-8')
727
842
  (views_dir / 'consumo_diario.sql').write_text(consumo_diario_sql, encoding='utf-8')
728
843
  (views_dir / 'consumo_mensual.sql').write_text(consumo_mensual_sql, encoding='utf-8')
729
844
  (views_dir / 'modelo_disponible.sql').write_text(modelo_disponible_sql, encoding='utf-8')
730
845
  (views_dir / 'tarifa_historico.sql').write_text(tarifa_historico_sql, encoding='utf-8')
731
- (views_dir / 'tarifa_vigente.sql').write_text(tarifa_vigente_sql, encoding='utf-8')
732
846
  (views_dir / 'tokens_mensaje.sql').write_text(tokens_mensaje_sql, encoding='utf-8')
733
- (views_dir / 'user_stats.sql').write_text(user_stats_sql, encoding='utf-8')
734
847
  (views_dir / 'usuario_consumo.sql').write_text(usuario_consumo_sql, encoding='utf-8')
848
+ (views_dir / 'valoracion.sql').write_text(valoracion_sql, encoding='utf-8')
@@ -221,10 +221,28 @@ class {{ model.name }}Read(PrettyModel):
221
221
 
222
222
  {% if not model.is_view %}
223
223
  @classmethod
224
+ {% if _all_vector_cols %}
225
+ def from_created_instance(
226
+ cls,
227
+ instance: {{ model.name }},
228
+ included: set[str],
229
+ excluded: str=None,
230
+ include_vectors: bool = False,
231
+ ) -> {{ model.name }}Read:
232
+ {% else %}
224
233
  def from_created_instance(cls, instance: {{ model.name }}, included: set[str], excluded: str=None) -> {{ model.name }}Read:
234
+ {% endif %}
235
+ {% if _all_vector_cols %}
236
+ # Como `from_instance`: los vectores solo si se piden. Son miles de números por fila, y
237
+ # lo que se acaba de crear se devuelve entero (`create_many` de mil filas eran millones).
238
+ # Las filas anidadas los dejan fuera siempre, como las relaciones de `from_instance`.
239
+ {% endif %}
225
240
  dto_data = {
226
241
  {% for column in model.columns %}
227
- {% if not column.hash %}
242
+ {% if column.hash %}
243
+ {% elif column.type == 'vector' %}
244
+ '{{ column.name }}': instance.{{ column.name }} if include_vectors else None,
245
+ {% else %}
228
246
  '{{ column.name }}': instance.{{ column.name }},
229
247
  {% endif %}
230
248
  {% endfor %}
@@ -139,11 +139,15 @@
139
139
  return len({{ await_keyword -}} cls.create_many([{{ model.name }}Create.from_dict(record) for record in records_data]))
140
140
  {% endif %}
141
141
 
142
+ {% set _created_vector_cols = model.columns | selectattr('type', 'equalto', 'vector') | list %}
142
143
  @classmethod
143
144
  @{{ error_handler_name }}
144
145
  {{ def_keyword }} create(
145
146
  cls,
146
147
  {{ model.tablename }}: {{ model.name }}Create,
148
+ {% if _created_vector_cols %}
149
+ include_vectors: bool = False,
150
+ {% endif %}
147
151
  session: Optional[{{ session_type }}] = None
148
152
  ) -> {{ model.name }}Read:
149
153
  """
@@ -151,6 +155,10 @@
151
155
 
152
156
  Args:
153
157
  {{ model.tablename }}: Datos del {{ model.tablename }} a crear
158
+ {% if _created_vector_cols %}
159
+ include_vectors: Si True, el registro devuelto lleva sus columnas vectoriales. Por
160
+ defecto no, como en `find`: se guardan igual, solo no se devuelven.
161
+ {% endif %}
154
162
  session: Sesión existente (opcional)
155
163
 
156
164
  Returns:
@@ -173,7 +181,11 @@
173
181
  {% endif %}
174
182
  {{ await_keyword -}} session.flush()
175
183
  {{ await_keyword -}} cls._on_create_after(new_instance, included, session)
184
+ {% if _created_vector_cols %}
185
+ return {{ model.name }}Read.from_created_instance(new_instance, included, include_vectors=include_vectors)
186
+ {% else %}
176
187
  return {{ model.name }}Read.from_created_instance(new_instance, included)
188
+ {% endif %}
177
189
 
178
190
  if session is not None:
179
191
  data = {{ await_keyword -}} _execute_create(session)
@@ -199,6 +211,9 @@
199
211
  {{ def_keyword }} create_many(
200
212
  cls,
201
213
  records: List[{{ model.name }}Create],
214
+ {% if _created_vector_cols %}
215
+ include_vectors: bool = False,
216
+ {% endif %}
202
217
  session: Optional[{{ session_type }}] = None
203
218
  ) -> List[{{ model.name }}Read]:
204
219
  """
@@ -206,6 +221,10 @@
206
221
 
207
222
  Args:
208
223
  records: Lista de {{ model.name }}Create con los datos de los registros
224
+ {% if _created_vector_cols %}
225
+ include_vectors: Si True, los registros devueltos llevan sus columnas vectoriales. Por
226
+ defecto no, como en `find_many`: con mil filas serían millones de números.
227
+ {% endif %}
209
228
  session: Sesión existente (opcional)
210
229
 
211
230
  Returns:
@@ -240,7 +259,11 @@
240
259
  # Los DTO se construyen después de los triggers `after`, como en `create`: lo que
241
260
  # escriban en la fila tiene que salir en lo que se devuelve
242
261
  return [
262
+ {% if _created_vector_cols %}
263
+ {{ model.name }}Read.from_created_instance(instance, included, include_vectors=include_vectors)
264
+ {% else %}
243
265
  {{ model.name }}Read.from_created_instance(instance, included)
266
+ {% endif %}
244
267
  for instance, included in creadas
245
268
  ]
246
269
 
File without changes
File without changes
File without changes