whatap-python 2.0.0rc1__tar.gz → 2.0.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 (220) hide show
  1. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/PKG-INFO +5 -1
  2. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/setup.py +6 -0
  3. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/__init__.py +84 -28
  4. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/agent/darwin/amd64/whatap_python +0 -0
  5. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/agent/darwin/arm64/whatap_python +0 -0
  6. whatap_python-2.0.2/whatap/agent/linux/amd64/whatap_python +0 -0
  7. {whatap_python-2.0.0rc1/whatap/agent/linux/amd64 → whatap_python-2.0.2/whatap/agent/linux/arm64}/whatap_python +0 -0
  8. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/agent/windows/whatap_python.exe +0 -0
  9. whatap_python-2.0.2/whatap/build.py +4 -0
  10. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/conf/configuration.py +2 -4
  11. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/counter/__init__.py +6 -1
  12. whatap_python-2.0.2/whatap/counter/tasks/__init__.py +3 -0
  13. whatap_python-2.0.2/whatap/counter/tasks/llm_log_sink_task.py +244 -0
  14. whatap_python-2.0.2/whatap/counter/tasks/llm_stat_task.py +77 -0
  15. whatap_python-2.0.2/whatap/llm/__init__.py +1 -0
  16. whatap_python-2.0.2/whatap/llm/definitions.py +43 -0
  17. whatap_python-2.0.2/whatap/llm/log_sink_packs/__init__.py +8 -0
  18. whatap_python-2.0.2/whatap/llm/log_sink_packs/llm_input_message.py +16 -0
  19. whatap_python-2.0.2/whatap/llm/log_sink_packs/llm_log_sink_pack.py +64 -0
  20. whatap_python-2.0.2/whatap/llm/log_sink_packs/llm_output_message.py +19 -0
  21. whatap_python-2.0.2/whatap/llm/log_sink_packs/llm_step_status.py +114 -0
  22. whatap_python-2.0.2/whatap/llm/log_sink_packs/llm_system_message.py +16 -0
  23. whatap_python-2.0.2/whatap/llm/log_sink_packs/llm_tool_calls.py +44 -0
  24. whatap_python-2.0.2/whatap/llm/log_sink_packs/llm_tool_results.py +16 -0
  25. whatap_python-2.0.2/whatap/llm/log_sink_packs/llm_tx_status.py +102 -0
  26. whatap_python-2.0.2/whatap/llm/pricing.py +236 -0
  27. whatap_python-2.0.2/whatap/llm/providers/anthropic/__init__.py +37 -0
  28. whatap_python-2.0.2/whatap/llm/providers/anthropic/messages/messages.py +67 -0
  29. whatap_python-2.0.2/whatap/llm/providers/anthropic/messages/messages_context.py +76 -0
  30. whatap_python-2.0.2/whatap/llm/providers/anthropic/messages/messages_extractor.py +123 -0
  31. whatap_python-2.0.2/whatap/llm/providers/interceptor.py +85 -0
  32. {whatap_python-2.0.0rc1/whatap/trace/mod/llm → whatap_python-2.0.2/whatap/llm/providers}/openai/__init__.py +33 -4
  33. whatap_python-2.0.2/whatap/llm/providers/openai/chat/chat.py +79 -0
  34. whatap_python-2.0.2/whatap/llm/providers/openai/chat/chat_context.py +78 -0
  35. whatap_python-2.0.2/whatap/llm/providers/openai/chat/chat_extractor.py +126 -0
  36. whatap_python-2.0.2/whatap/llm/providers/openai/completions/completions.py +67 -0
  37. whatap_python-2.0.2/whatap/llm/providers/openai/completions/completions_context.py +32 -0
  38. whatap_python-2.0.2/whatap/llm/providers/openai/completions/completions_extractor.py +61 -0
  39. whatap_python-2.0.0rc1/whatap/trace/mod/llm/openai/util.py → whatap_python-2.0.2/whatap/llm/providers/openai/content_parser.py +9 -1
  40. whatap_python-2.0.2/whatap/llm/providers/openai/embeddings/embeddings.py +54 -0
  41. whatap_python-2.0.2/whatap/llm/providers/openai/embeddings/embeddings_context.py +26 -0
  42. whatap_python-2.0.2/whatap/llm/providers/openai/embeddings/embeddings_extractor.py +26 -0
  43. whatap_python-2.0.2/whatap/llm/providers/openai/responses/responses.py +67 -0
  44. whatap_python-2.0.2/whatap/llm/providers/openai/responses/responses_context.py +88 -0
  45. whatap_python-2.0.2/whatap/llm/providers/openai/responses/responses_extractor.py +125 -0
  46. whatap_python-2.0.2/whatap/llm/providers/stream_accumulator.py +73 -0
  47. whatap_python-2.0.2/whatap/llm/stats/__init__.py +15 -0
  48. whatap_python-2.0.0rc1/whatap/counter/tasks/llm_active_stat.py → whatap_python-2.0.2/whatap/llm/stats/active_stat.py +21 -20
  49. whatap_python-2.0.2/whatap/llm/stats/api_status_stat.py +28 -0
  50. whatap_python-2.0.0rc1/whatap/counter/tasks/llm_api_status_stat.py → whatap_python-2.0.2/whatap/llm/stats/base_stat.py +20 -24
  51. whatap_python-2.0.2/whatap/llm/stats/error_stat.py +56 -0
  52. whatap_python-2.0.2/whatap/llm/stats/feature_stat.py +63 -0
  53. whatap_python-2.0.2/whatap/llm/stats/meter.py +18 -0
  54. whatap_python-2.0.2/whatap/llm/stats/perf_stat.py +105 -0
  55. whatap_python-2.0.2/whatap/llm/stats/token_usage_stat.py +104 -0
  56. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/net/async_sender.py +19 -2
  57. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/net/udp_session.py +60 -16
  58. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/net/udp_thread.py +2 -4
  59. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/scripts/__init__.py +6 -6
  60. whatap_python-2.0.2/whatap/trace/mod/application/fastapi/__init__.py +31 -0
  61. whatap_python-2.0.2/whatap/trace/mod/application/fastapi/endpoint.py +73 -0
  62. whatap_python-2.0.2/whatap/trace/mod/application/fastapi/exception_log.py +63 -0
  63. whatap_python-2.0.2/whatap/trace/mod/application/fastapi/instrumentation.py +204 -0
  64. whatap_python-2.0.2/whatap/trace/mod/application/fastapi/scope.py +115 -0
  65. whatap_python-2.0.2/whatap/trace/mod/application/fastapi/transaction.py +67 -0
  66. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/application/wsgi.py +4 -1
  67. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/database/util.py +32 -14
  68. whatap_python-2.0.2/whatap/trace/mod/email/__init__.py +0 -0
  69. whatap_python-2.0.2/whatap/trace/mod/httpc/__init__.py +0 -0
  70. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/httpc/util.py +70 -33
  71. whatap_python-2.0.2/whatap/trace/mod/standalone/__init__.py +0 -0
  72. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/standalone/multiple.py +4 -1
  73. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/standalone/single.py +4 -1
  74. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/trace_context.py +1 -0
  75. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/trace_context_manager.py +40 -1
  76. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/trace_handler.py +0 -5
  77. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/trace_module_definition.py +2 -2
  78. whatap_python-2.0.2/whatap/util/__init__.py +0 -0
  79. whatap_python-2.0.2/whatap/util/cardinality/__init__.py +0 -0
  80. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap_python.egg-info/PKG-INFO +5 -1
  81. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap_python.egg-info/SOURCES.txt +55 -17
  82. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap_python.egg-info/requires.txt +4 -0
  83. whatap_python-2.0.0rc1/whatap/agent/linux/arm64/whatap_python +0 -0
  84. whatap_python-2.0.0rc1/whatap/build.py +0 -4
  85. whatap_python-2.0.0rc1/whatap/counter/tasks/__init__.py +0 -17
  86. whatap_python-2.0.0rc1/whatap/counter/tasks/llm_error_stat.py +0 -88
  87. whatap_python-2.0.0rc1/whatap/counter/tasks/llm_feature_stat.py +0 -91
  88. whatap_python-2.0.0rc1/whatap/counter/tasks/llm_meter.py +0 -57
  89. whatap_python-2.0.0rc1/whatap/counter/tasks/llm_perf_stat.py +0 -132
  90. whatap_python-2.0.0rc1/whatap/counter/tasks/llm_token_usage_stat.py +0 -122
  91. whatap_python-2.0.0rc1/whatap/trace/mod/application/fastapi.py +0 -476
  92. whatap_python-2.0.0rc1/whatap/trace/mod/llm/anthropic.py +0 -488
  93. whatap_python-2.0.0rc1/whatap/trace/mod/llm/llm_log_sink_pack.py +0 -316
  94. whatap_python-2.0.0rc1/whatap/trace/mod/llm/llm_sender.py +0 -527
  95. whatap_python-2.0.0rc1/whatap/trace/mod/llm/openai/chat.py +0 -356
  96. whatap_python-2.0.0rc1/whatap/trace/mod/llm/openai/embeddings.py +0 -178
  97. whatap_python-2.0.0rc1/whatap/trace/mod/llm/openai/responses.py +0 -409
  98. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/README.md +0 -0
  99. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/pyproject.toml +0 -0
  100. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/setup.cfg +0 -0
  101. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/LICENSE +0 -0
  102. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/README.rst +0 -0
  103. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/__main__.py +0 -0
  104. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/bootstrap/__init__.py +0 -0
  105. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/bootstrap/sitecustomize.py +0 -0
  106. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/conf/__init__.py +0 -0
  107. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/conf/configure.py +0 -0
  108. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/conf/license.py +0 -0
  109. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/control/__init__.py +0 -0
  110. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/counter/counter_manager.py +0 -0
  111. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/counter/tasks/base_task.py +0 -0
  112. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/counter/tasks/openfiledescriptor.py +0 -0
  113. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/io/__init__.py +0 -0
  114. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/io/data_inputx.py +0 -0
  115. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/io/data_outputx.py +0 -0
  116. {whatap_python-2.0.0rc1/whatap/net → whatap_python-2.0.2/whatap/llm/providers}/__init__.py +0 -0
  117. {whatap_python-2.0.0rc1/whatap/pack → whatap_python-2.0.2/whatap/llm/providers/anthropic/messages}/__init__.py +0 -0
  118. {whatap_python-2.0.0rc1/whatap/trace/mod → whatap_python-2.0.2/whatap/llm/providers/openai/chat}/__init__.py +0 -0
  119. {whatap_python-2.0.0rc1/whatap/trace/mod/amqp → whatap_python-2.0.2/whatap/llm/providers/openai/completions}/__init__.py +0 -0
  120. {whatap_python-2.0.0rc1/whatap/trace/mod/application → whatap_python-2.0.2/whatap/llm/providers/openai/embeddings}/__init__.py +0 -0
  121. {whatap_python-2.0.0rc1/whatap/trace/mod/database → whatap_python-2.0.2/whatap/llm/providers/openai/responses}/__init__.py +0 -0
  122. {whatap_python-2.0.0rc1/whatap/trace/mod/email → whatap_python-2.0.2/whatap/net}/__init__.py +0 -0
  123. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/net/packet_enum.py +0 -0
  124. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/net/packet_type_enum.py +0 -0
  125. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/net/param_def.py +0 -0
  126. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/net/stackhelper.py +0 -0
  127. {whatap_python-2.0.0rc1/whatap/trace/mod/httpc → whatap_python-2.0.2/whatap/pack}/__init__.py +0 -0
  128. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/pack/logSinkPack.py +0 -0
  129. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/pack/pack.py +0 -0
  130. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/pack/pack_enum.py +0 -0
  131. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/pack/tagCountPack.py +0 -0
  132. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/__init__.py +0 -0
  133. {whatap_python-2.0.0rc1/whatap/trace/mod/llm → whatap_python-2.0.2/whatap/trace/mod}/__init__.py +0 -0
  134. {whatap_python-2.0.0rc1/whatap/trace/mod/standalone → whatap_python-2.0.2/whatap/trace/mod/amqp}/__init__.py +0 -0
  135. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/amqp/kombu.py +0 -0
  136. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/amqp/pika.py +0 -0
  137. {whatap_python-2.0.0rc1/whatap/util → whatap_python-2.0.2/whatap/trace/mod/application}/__init__.py +0 -0
  138. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/application/bottle.py +0 -0
  139. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/application/celery.py +0 -0
  140. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/application/cherrypy.py +0 -0
  141. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/application/django.py +0 -0
  142. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/application/django_asgi.py +0 -0
  143. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/application/django_py3.py +0 -0
  144. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/application/flask.py +0 -0
  145. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/application/frappe.py +0 -0
  146. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/application/graphql.py +0 -0
  147. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/application/nameko.py +0 -0
  148. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/application/odoo.py +0 -0
  149. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/application/starlette.py +0 -0
  150. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/application/tornado.py +0 -0
  151. {whatap_python-2.0.0rc1/whatap/util/cardinality → whatap_python-2.0.2/whatap/trace/mod/database}/__init__.py +0 -0
  152. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/database/cxoracle.py +0 -0
  153. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/database/mongo.py +0 -0
  154. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/database/mysql.py +0 -0
  155. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/database/neo4j.py +0 -0
  156. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/database/psycopg2.py +0 -0
  157. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/database/psycopg3.py +0 -0
  158. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/database/redis.py +0 -0
  159. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/database/sqlalchemy.py +0 -0
  160. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/database/sqlite3.py +0 -0
  161. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/email/smtp.py +0 -0
  162. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/httpc/django.py +0 -0
  163. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/httpc/httplib.py +0 -0
  164. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/httpc/httpx.py +0 -0
  165. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/httpc/requests.py +0 -0
  166. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/httpc/urllib3.py +0 -0
  167. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/logging.py +0 -0
  168. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/mod/plugin.py +0 -0
  169. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/simple_trace_context.py +0 -0
  170. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/trace_error.py +0 -0
  171. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/trace/trace_import.py +0 -0
  172. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/bit_util.py +0 -0
  173. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/cardinality/hyperloglog.py +0 -0
  174. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/cardinality/murmurhash.py +0 -0
  175. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/cardinality/registerset.py +0 -0
  176. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/compare_util.py +0 -0
  177. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/date_util.py +0 -0
  178. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/debug_util.py +0 -0
  179. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/escape_literal_sql.py +0 -0
  180. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/frame_util.py +0 -0
  181. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/hash_util.py +0 -0
  182. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/hexa32.py +0 -0
  183. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/int_set.py +0 -0
  184. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/ip_util.py +0 -0
  185. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/keygen.py +0 -0
  186. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/linked_list.py +0 -0
  187. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/linked_map.py +0 -0
  188. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/metering_util.py +0 -0
  189. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/request_double_queue.py +0 -0
  190. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/request_queue.py +0 -0
  191. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/string_util.py +0 -0
  192. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/throttle_util.py +0 -0
  193. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/util/userid_util.py +0 -0
  194. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/__init__.py +0 -0
  195. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/blob_value.py +0 -0
  196. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/boolean_value.py +0 -0
  197. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/decimal_value.py +0 -0
  198. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/double_summary.py +0 -0
  199. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/double_value.py +0 -0
  200. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/float_array.py +0 -0
  201. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/float_value.py +0 -0
  202. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/int_array.py +0 -0
  203. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/ip4_value.py +0 -0
  204. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/list_value.py +0 -0
  205. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/long_array.py +0 -0
  206. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/long_summary.py +0 -0
  207. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/map_value.py +0 -0
  208. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/null_value.py +0 -0
  209. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/number_value.py +0 -0
  210. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/summary_value.py +0 -0
  211. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/text_array.py +0 -0
  212. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/text_hash_value.py +0 -0
  213. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/text_value.py +0 -0
  214. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/value.py +0 -0
  215. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/value/value_enum.py +0 -0
  216. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap/whatap.conf +0 -0
  217. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap_python.egg-info/dependency_links.txt +0 -0
  218. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap_python.egg-info/entry_points.txt +0 -0
  219. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap_python.egg-info/not-zip-safe +0 -0
  220. {whatap_python-2.0.0rc1 → whatap_python-2.0.2}/whatap_python.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: whatap-python
3
- Version: 2.0.0rc1
3
+ Version: 2.0.2
4
4
  Summary: Monitoring and Profiling Service
5
5
  Home-page: https://www.whatap.io
6
6
  Author: whatap
@@ -22,12 +22,16 @@ Classifier: Programming Language :: Python :: 3.12
22
22
  Classifier: Programming Language :: Python :: 3.13
23
23
  Requires-Python: >=3.7
24
24
  Requires-Dist: psutil>=5.0.0; platform_system == "Windows"
25
+ Provides-Extra: llm
26
+ Requires-Dist: datasketches>=5.2.0; extra == "llm"
27
+ Requires-Dist: genai-prices; extra == "llm"
25
28
  Dynamic: author
26
29
  Dynamic: author-email
27
30
  Dynamic: classifier
28
31
  Dynamic: description
29
32
  Dynamic: home-page
30
33
  Dynamic: license
34
+ Dynamic: provides-extra
31
35
  Dynamic: requires-dist
32
36
  Dynamic: requires-python
33
37
  Dynamic: summary
@@ -35,6 +35,12 @@ setup(name=build.name,
35
35
  install_requires=[
36
36
  'psutil>=5.0.0; platform_system=="Windows"', # Required for Windows compatibility (memory/process monitoring)
37
37
  ],
38
+ extras_require={
39
+ 'llm': [
40
+ 'datasketches>=5.2.0',
41
+ 'genai-prices',
42
+ ],
43
+ },
38
44
  classifiers=[
39
45
  'Development Status :: 4 - Beta',
40
46
  'Intended Audience :: Developers',
@@ -59,7 +59,7 @@ def preview_whatap_conf(option_name:str):
59
59
  - standalone_enabled (False)
60
60
  - counter_thread_enabled (False)
61
61
  """
62
- value = 'false'
62
+ value = None
63
63
  try:
64
64
  with open(whatap_config) as f:
65
65
  for raw in f:
@@ -71,15 +71,22 @@ def preview_whatap_conf(option_name:str):
71
71
  if len(parts) == 2:
72
72
  value = parts[1].strip()
73
73
  break
74
- return value
75
74
 
76
75
  except FileNotFoundError:
77
- return value
76
+ pass
78
77
 
79
78
  except Exception as e:
80
79
  print(f'WHATAP: config parse error ({e!r})')
80
+
81
+ if value is not None:
81
82
  return value
82
83
 
84
+ env_value = os.environ.get(option_name)
85
+ if env_value is not None:
86
+ return env_value
87
+
88
+ return 'false'
89
+
83
90
 
84
91
 
85
92
  ignore_whatap_stdout = preview_whatap_conf("ignore_whatap_stdout")
@@ -298,15 +305,15 @@ def hooks(home):
298
305
  try:
299
306
  for key, value_list in DEFINITION.items():
300
307
  for value in value_list:
301
- if len(value) == 3 and not value[2]:
308
+ if len(value) >= 3 and isinstance(value[2], str):
309
+ module_path = value[2]
310
+ elif len(value) == 3 and not value[2]:
302
311
  continue
303
-
312
+ else:
313
+ module_path = '{0}.{1}.{2}.{3}'.format(
314
+ 'whatap', 'trace', 'mod', key)
304
315
  IMPORT_HOOKS[value[0]] = {'def': value[1],
305
- 'module': '{0}.{1}.{2}.{3}'.format(
306
- 'whatap',
307
- 'trace',
308
- 'mod',
309
- key)}
316
+ 'module': module_path}
310
317
  except Exception as e:
311
318
  logging.debug(e, extra={'id': 'MODULE ERROR'})
312
319
  finally:
@@ -418,15 +425,6 @@ def go(batch=False, opts={}, llm=False):
418
425
  if llm:
419
426
  home = 'WHATAP_HOME'
420
427
  file_name = AGENT_NAME + '.pid.llm'
421
- # LLM Go Agent용 별도 UDP 포트
422
- from whatap.conf.configure import Configure as conf
423
- llm_port = int(getattr(conf, 'llm_net_udp_port', 0))
424
- if not llm_port:
425
- llm_port = int(conf.net_udp_port) + 100
426
- conf.llm_net_udp_port = llm_port
427
- newenv['WHATAP_NET_UDP_PORT'] = str(llm_port)
428
- # whatap.conf에 기록하여 Go Agent가 읽을 수 있도록 함
429
- update_config(home, 'llm_net_udp_port', str(llm_port))
430
428
  elif not batch:
431
429
  home = 'WHATAP_HOME'
432
430
  file_name = AGENT_NAME + '.pid'
@@ -537,12 +535,47 @@ def go(batch=False, opts={}, llm=False):
537
535
  label = 'LLM golang module' if llm else 'golang module'
538
536
 
539
537
  if sys.platform == 'win32':
540
- DETACHED_PROCESS = 0x00000008
538
+ # SESSIONNAME 환경변수는 NSSM이 부모 세션에서 상속받아 부정확함
539
+ # ProcessIdToSessionId로 실제 프로세스 세션 ID 확인
540
+ try:
541
+ import ctypes
542
+ _sid = ctypes.c_ulong(0)
543
+ ctypes.windll.kernel32.ProcessIdToSessionId(
544
+ ctypes.windll.kernel32.GetCurrentProcessId(),
545
+ ctypes.byref(_sid)
546
+ )
547
+ is_session0 = (_sid.value == 0)
548
+ except Exception:
549
+ is_session0 = not os.environ.get('SESSIONNAME')
550
+
541
551
  cmd_args.append('foreground')
542
- process = subprocess.Popen(cmd_args,
543
- cwd=home_path, env=newenv,
544
- creationflags=DETACHED_PROCESS,
545
- stdout=subprocess.PIPE, stderr=subprocess.PIPE)
552
+
553
+ if is_session0:
554
+ # Session 0에서만 WHATAP_FOREGROUND=1 추가 주입.
555
+ # IsAnInteractiveSession()이 false로 떨어지는 경로에서
556
+ # foreground CLI 인자를 보강하는 안전망.
557
+ newenv['WHATAP_FOREGROUND'] = '1'
558
+
559
+ # Session 0에서는 PIPE를 읽지 않으면 버퍼 고갈로 Go 프로세스가 블로킹됨
560
+ # 로그 파일로 리디렉션
561
+ # CREATE_NO_WINDOW: Session 0 서비스 컨텍스트에 적합한 플래그
562
+ CREATE_NO_WINDOW = 0x08000000
563
+ log_dir = os.path.join(home_path, 'logs')
564
+ go_log_path = os.path.join(log_dir, AGENT_NAME + '.log')
565
+ try:
566
+ go_out = open(go_log_path, 'ab')
567
+ except Exception:
568
+ go_out = subprocess.DEVNULL
569
+ process = subprocess.Popen(cmd_args,
570
+ cwd=home_path, env=newenv,
571
+ creationflags=CREATE_NO_WINDOW,
572
+ stdout=go_out, stderr=go_out)
573
+ else:
574
+ DETACHED_PROCESS = 0x00000008
575
+ process = subprocess.Popen(cmd_args,
576
+ cwd=home_path, env=newenv,
577
+ creationflags=DETACHED_PROCESS,
578
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
546
579
  else:
547
580
  process = subprocess.Popen(cmd_args,
548
581
  cwd=home_path, env=newenv,
@@ -551,8 +584,11 @@ def go(batch=False, opts={}, llm=False):
551
584
 
552
585
  time.sleep(0.5)
553
586
  if process.poll() is not None:
554
- stdouts, errs = process.communicate()
555
- whatap_print("executed {} ".format(label), str(stdouts,"utf8"), str(errs, "utf8"))
587
+ if sys.platform == 'win32' and is_session0:
588
+ whatap_print("executed {} (exit code: {}, log: {})".format(label, process.returncode, go_log_path))
589
+ else:
590
+ stdouts, errs = process.communicate()
591
+ whatap_print("executed {} ".format(label), str(stdouts,"utf8"), str(errs, "utf8"))
556
592
  else:
557
593
  write_file(home, file_name, str(process.pid))
558
594
  whatap_print("executed {} in background (PID: {})".format(label, process.pid))
@@ -690,8 +726,10 @@ except ImportError:
690
726
  if sys.platform == 'win32':
691
727
  import tempfile
692
728
  default_lock_file = os.path.join(tempfile.gettempdir(), 'whatap-python.lock')
729
+ default_llm_lock_file = os.path.join(tempfile.gettempdir(), 'whatap-python-llm.lock')
693
730
  else:
694
731
  default_lock_file = '/tmp/whatap-python.lock'
732
+ default_llm_lock_file = '/tmp/whatap-python-llm.lock'
695
733
 
696
734
  def openPortFile(filepath=os.environ.get('WHATAP_LOCK_FILE', default_lock_file)):
697
735
  f = None
@@ -732,12 +770,12 @@ def openPortFile(filepath=os.environ.get('WHATAP_LOCK_FILE', default_lock_file))
732
770
  whatap_print('WHATAP: file close failed: %s' % e)
733
771
  return None
734
772
 
735
- def get_port_number(port=6600, home=os.environ.get('WHATAP_HOME')):
773
+ def get_port_number(port=6600, home=os.environ.get('WHATAP_HOME'), lock_file=None):
736
774
  if not home:
737
775
  return None
738
776
 
739
777
  for i in range(100):
740
- f = openPortFile()
778
+ f = openPortFile(filepath=lock_file) if lock_file else openPortFile()
741
779
  if not f:
742
780
  if i > 50:
743
781
  time.sleep(0.1)
@@ -798,6 +836,24 @@ def configPort():
798
836
  return port
799
837
 
800
838
 
839
+ def configLlmPort():
840
+ # force_llm_net_udp_port가 설정된 경우 lock 파일 무시하고 해당 포트 강제 사용
841
+ force_port_str = preview_whatap_conf("force_llm_net_udp_port")
842
+ if force_port_str and force_port_str not in ('false', 'False'):
843
+ try:
844
+ port = int(force_port_str)
845
+ except ValueError:
846
+ whatap_print('WHATAP: force_llm_net_udp_port value is invalid: {}'.format(force_port_str))
847
+ port = None
848
+ else:
849
+ llm_lock_file = os.environ.get('WHATAP_LLM_LOCK_FILE', default_llm_lock_file)
850
+ port = get_port_number(port=6700, home=os.environ.get('WHATAP_HOME'), lock_file=llm_lock_file)
851
+
852
+ if port:
853
+ update_config('WHATAP_HOME', 'llm_net_udp_port', str(port))
854
+ return port
855
+
856
+
801
857
  def find_whatap_conf():
802
858
  # 1. 현재 디렉토리 검색
803
859
  script_dir = os.path.dirname(os.path.abspath(__file__))
@@ -0,0 +1,4 @@
1
+ app = 'Python'
2
+ name = 'whatap-python'
3
+ version = '2.0.2'
4
+ release_date = '20260428'
@@ -19,7 +19,6 @@ Configuration = {
19
19
  "trace_ignore_url_prefix": None,
20
20
  "trace_websocket_enabled": False,
21
21
 
22
- "trace_llm_log_enabled": False,
23
22
  "llm_api_hosts": "",
24
23
 
25
24
  "debug": False,
@@ -222,9 +221,8 @@ Configuration = {
222
221
  "open_file_descriptor_enabled": False,
223
222
  "open_file_descriptor_interval":60,
224
223
  "counter_thread_enabled": False,
225
- "llm_net_udp_port": 0,
226
- "llm_token_usage_stat_enabled" : False,
227
- "llm_token_usage_stat_interval" : 5,
224
+ "llm_net_udp_port": 6700,
225
+ "force_llm_net_udp_port": False,
228
226
  "llm_model_pricing": "",
229
227
  "llm_perf_sketch_enabled": True,
230
228
  "llm_perf_sketch_k": 200
@@ -6,4 +6,9 @@ counter_thread_enabled = preview_whatap_conf("counter_thread_enabled")
6
6
  if counter_thread_enabled != 'false':
7
7
  mgr = CounterMgr()
8
8
  mgr.setDaemon(True)
9
- mgr.start()
9
+ mgr.start()
10
+
11
+ from .tasks.llm_stat_task import LlmStatTask
12
+ llm_stat = LlmStatTask()
13
+ llm_stat.setDaemon(True)
14
+ llm_stat.start()
@@ -0,0 +1,3 @@
1
+ from whatap.counter.tasks.openfiledescriptor import OpenFileDescriptorTask
2
+
3
+ TASK_CLASSES = [OpenFileDescriptorTask]
@@ -0,0 +1,244 @@
1
+ import queue
2
+ import threading
3
+
4
+ from whatap import DateUtil
5
+ from whatap import logging
6
+ from whatap.pack import logSinkPack
7
+ from whatap.conf.configure import Configure as conf
8
+ from whatap.trace.trace_context_manager import TraceContextManager
9
+
10
+ import whatap.io as whatapio
11
+ import whatap.net.async_sender as async_sender
12
+
13
+ from whatap.llm.log_sink_packs.llm_log_sink_pack import LlmLogSinkPack
14
+ from whatap.llm.log_sink_packs.llm_step_status import LlmStepStatus
15
+ from whatap.llm.log_sink_packs.llm_system_message import LlmSystemMessage
16
+ from whatap.llm.log_sink_packs.llm_input_message import LlmInputMessage
17
+ from whatap.llm.log_sink_packs.llm_output_message import LlmOutputMessage
18
+ from whatap.llm.log_sink_packs.llm_tool_calls import LlmToolCalls
19
+ from whatap.llm.log_sink_packs.llm_tool_results import LlmToolResults
20
+ from whatap.llm.log_sink_packs.llm_tx_status import LlmTxStatus
21
+
22
+ _MAX_CONTENT_BYTES = 20000
23
+
24
+
25
+ class LlmLogSinkTask(object):
26
+ _instance = None
27
+ _lock = threading.Lock()
28
+
29
+ def __init__(self):
30
+ self._q = queue.Queue(4096)
31
+ self._started = False
32
+
33
+ @classmethod
34
+ def get_instance(cls):
35
+ if cls._instance is None:
36
+ with cls._lock:
37
+ if cls._instance is None:
38
+ cls._instance = cls()
39
+ return cls._instance
40
+
41
+ def dispatch(self, pack):
42
+ if not isinstance(pack, LlmStepStatus):
43
+ return
44
+
45
+ ctx = getattr(pack, '_trace_ctx', None) or TraceContextManager.getLocalContext()
46
+ pack.set_context(ctx)
47
+
48
+ if not pack.operation_type and ctx:
49
+ pack.operation_type = getattr(ctx, '_llm_operation_type', '') or 'unknown'
50
+
51
+ try:
52
+ pack.calculate_cost()
53
+ except Exception as e:
54
+ logging.warning('[LLM] calculate_cost failed: model=%s, input_tokens=%s, output_tokens=%s, cached_tokens=%s, error=%s'
55
+ % (pack.model, pack.input_tokens, pack.output_tokens, pack.cached_tokens, e),
56
+ extra={'id': 'LLM024'})
57
+
58
+ self._accumulate_tx_summary(ctx, pack)
59
+
60
+ if ctx and not pack.success and pack.error_type:
61
+ ctx._llm_last_error_type = pack.error_type
62
+ ctx._llm_last_error_model = pack.model or 'unknown'
63
+ ctx._llm_last_error_provider = pack.provider or ''
64
+ ctx._llm_last_error_op_type = pack.operation_type or 'unknown'
65
+ ctx._llm_last_error_url = pack.url or ''
66
+
67
+ self._ensure_started()
68
+ if self._q.full():
69
+ logging.warning('[LLM] send queue full, pack dropped: model=%s' % pack.model,
70
+ extra={'id': 'LLM025'})
71
+ return
72
+ self._q.put(pack)
73
+
74
+ def send_tx_status(self, ctx):
75
+ if not ctx:
76
+ return
77
+ if not getattr(conf, 'llm_enabled', False):
78
+ return
79
+ tx = getattr(ctx, '_llm_tx_status', None)
80
+ if not tx or tx.call_count == 0:
81
+ return
82
+
83
+ try:
84
+ self._send_log_sink(tx)
85
+ # 트랜잭션 경계에서 LLM 버퍼에 남아있는 데이터 flush (큐를 거쳐 순서 보장)
86
+ async_sender.flush_llm_relaypack()
87
+ except Exception as e:
88
+ logging.warning('[LLM] send_llm_tx_status failed: %s' % e, extra={'id': 'LLM022'})
89
+
90
+ # ── internal ──
91
+
92
+ def _ensure_started(self):
93
+ if self._started:
94
+ return
95
+ with self._lock:
96
+ if self._started:
97
+ return
98
+ self._started = True
99
+ t = threading.Thread(target=self._run, daemon=True)
100
+ t.start()
101
+
102
+ def _run(self):
103
+ while True:
104
+ pack = self._q.get()
105
+ if not pack:
106
+ continue
107
+ try:
108
+ self._process_pack(pack)
109
+ except Exception as e:
110
+ logging.warning('[LLM] process failed: %s' % e, extra={'id': 'LLM003'})
111
+
112
+ def _process_pack(self, pack):
113
+ # llm_step_status
114
+ self._send_log_sink(pack)
115
+
116
+ # system_message
117
+ for text in getattr(pack, 'system_texts', None) or []:
118
+ self._send_log_sink(LlmSystemMessage.from_pack(pack, system_text=text))
119
+
120
+ # input_message
121
+ prompt_text = getattr(pack, 'prompt_text', '') or ''
122
+ self._send_log_sink(LlmInputMessage.from_pack(pack, prompt_text=prompt_text))
123
+
124
+ # output_message
125
+ completion_text = getattr(pack, 'completion_text', '') or ''
126
+ reasoning_text = getattr(pack, 'reasoning_text', '') or ''
127
+ self._send_log_sink(LlmOutputMessage.from_pack(pack,
128
+ completion_text=completion_text, reasoning_text=reasoning_text))
129
+
130
+ # tool
131
+ tool_calls_text = getattr(pack, 'tool_calls_text', '') or ''
132
+ if tool_calls_text:
133
+ self._send_log_sink(LlmToolCalls.from_pack(pack, tool_calls_text=tool_calls_text))
134
+
135
+ # tool_result
136
+ tool_results_text = getattr(pack, 'tool_results_text', '') or ''
137
+ if tool_results_text:
138
+ self._send_log_sink(LlmToolResults.from_pack(pack, tool_results_text=tool_results_text))
139
+
140
+ def _accumulate_tx_summary(self, ctx, pack):
141
+ if not ctx:
142
+ return
143
+ tx = getattr(ctx, '_llm_tx_status', None)
144
+ if tx is None:
145
+ tx = LlmTxStatus()
146
+ tx.txid = str(ctx.id)
147
+ ctx._llm_tx_status = tx
148
+ tx.accumulate(pack)
149
+
150
+ def _send_log_sink(self, data):
151
+ tags = data.tags()
152
+ fields = data.fields()
153
+ content = data.content()
154
+ if content:
155
+ self._send_chunked(tags, fields, content, data.index)
156
+ else:
157
+ self._send_pack(tags, fields, '', data.index)
158
+
159
+ def _send_pack(self, tags, fields, content, index):
160
+ try:
161
+ tags = {k: v for k, v in tags.items() if v is not None}
162
+ fields = {k: v for k, v in fields.items() if v is not None}
163
+ fields['index'] = index
164
+ if content:
165
+ encoded = content.encode('utf-8', errors='replace')
166
+ if len(encoded) > _MAX_CONTENT_BYTES:
167
+ content = encoded[:_MAX_CONTENT_BYTES].decode('utf-8', errors='replace')
168
+ p = logSinkPack.getLogSinkPack(
169
+ t=DateUtil.now(),
170
+ category=LlmLogSinkPack.CATEGORY,
171
+ tags=tags,
172
+ fields={},
173
+ line=DateUtil.now(),
174
+ content=content
175
+ )
176
+ for k, v in fields.items():
177
+ if isinstance(v, (int, float)):
178
+ p.fields.putAuto(k, v)
179
+ else:
180
+ p.fields.putString(k, str(v))
181
+
182
+ p.pcode = conf.PCODE
183
+ bout = whatapio.DataOutputX()
184
+ bout.writePack(p, None)
185
+ packbytes = bout.toByteArray()
186
+ async_sender.send_llm_relaypack(packbytes)
187
+ except Exception as e:
188
+ logging.warning('[LLM] send_pack failed: %s' % e, extra={'id': 'LLM001'})
189
+
190
+ def _send_chunked(self, tags, fields, content, index):
191
+ try:
192
+ if not content:
193
+ self._send_pack(tags, fields, '', index)
194
+ return
195
+
196
+ tags = {k: v for k, v in tags.items() if v is not None}
197
+ fields = {k: v for k, v in fields.items() if v is not None}
198
+
199
+ content_bytes = content.encode('utf-8', errors='replace')
200
+ total_bytes = len(content_bytes)
201
+
202
+ if total_bytes <= _MAX_CONTENT_BYTES:
203
+ chunk_fields = dict(fields)
204
+ chunk_fields["chunk_index"] = 0
205
+ chunk_fields["chunk_total"] = 1
206
+ self._send_pack(tags, chunk_fields, content, index)
207
+ return
208
+
209
+ raw_chunks = []
210
+ i = 0
211
+ while i < total_bytes:
212
+ end = min(i + _MAX_CONTENT_BYTES, total_bytes)
213
+ if end < total_bytes:
214
+ while end > i and (content_bytes[end] & 0xC0) == 0x80:
215
+ end -= 1
216
+ raw_chunks.append(content_bytes[i:end].decode('utf-8', errors='replace'))
217
+ i = end
218
+
219
+ chunk_total = len(raw_chunks)
220
+ for idx, chunk in enumerate(raw_chunks):
221
+ chunk_fields = dict(fields)
222
+ chunk_fields["chunk_index"] = idx
223
+ chunk_fields["chunk_total"] = chunk_total
224
+ self._send_pack(tags, chunk_fields, chunk, index)
225
+ except Exception as e:
226
+ logging.warning('[LLM] send_chunked failed: %s' % e, extra={'id': 'LLM002'})
227
+
228
+
229
+ # ── module-level API ──
230
+
231
+ def dispatch_llm_pack(pack):
232
+ LlmLogSinkTask.get_instance().dispatch(pack)
233
+
234
+
235
+ def send_llm_tx_status(ctx):
236
+ LlmLogSinkTask.get_instance().send_tx_status(ctx)
237
+
238
+
239
+ def send_llm_pack(metadata):
240
+ pack = LlmStepStatus()
241
+ for key, val in metadata.items():
242
+ if hasattr(pack, key):
243
+ setattr(pack, key, val)
244
+ dispatch_llm_pack(pack)
@@ -0,0 +1,77 @@
1
+ import time
2
+ import logging
3
+ from threading import Thread
4
+
5
+ from whatap.llm.stats import LLM_STAT_CLASSES
6
+
7
+
8
+ class LlmStatTask(Thread):
9
+ _instance = None
10
+
11
+ def __init__(self):
12
+ super(LlmStatTask, self).__init__()
13
+ self.stats = list()
14
+ self._stat_map = {}
15
+ self.last_executed = {}
16
+ LlmStatTask._instance = self
17
+
18
+ def _register_stats(self):
19
+ for cls in LLM_STAT_CLASSES:
20
+ stat = cls()
21
+ self.stats.append(stat)
22
+ self._stat_map[stat.name()] = stat
23
+ self.last_executed[stat.name()] = 0
24
+
25
+ def run(self):
26
+ self._register_stats()
27
+
28
+ while True:
29
+ current_time = time.time()
30
+ time.sleep(1)
31
+ for stat in self.stats:
32
+ last_executed_time = self.last_executed[stat.name()]
33
+ interval = stat.interval()
34
+
35
+ if current_time - last_executed_time >= interval:
36
+ try:
37
+ self.last_executed[stat.name()] = current_time
38
+ stat.process()
39
+ except Exception as e:
40
+ logging.debug(e, extra={'id': 'WA182'}, exc_info=True)
41
+
42
+ def notify(self, pack):
43
+ from whatap.conf.configure import Configure as conf
44
+ if not getattr(conf, 'llm_enabled', False):
45
+ return
46
+ for stat in self.stats:
47
+ try:
48
+ stat.update_from_pack(pack)
49
+ except Exception as e:
50
+ logging.debug(e, extra={'id': 'WA183'}, exc_info=True)
51
+
52
+ if pack.success:
53
+ from whatap.llm.stats.meter import Meter
54
+ Meter.increment()
55
+
56
+ def flush_last_error(self, ctx):
57
+ last_error_type = getattr(ctx, '_llm_last_error_type', None)
58
+ if not last_error_type:
59
+ return
60
+ from whatap.conf.configure import Configure as conf
61
+ if not getattr(conf, 'llm_enabled', False):
62
+ return
63
+ stat = self._stat_map.get('ErrorStat')
64
+ if stat:
65
+ stat.update_last_error(
66
+ getattr(ctx, '_llm_last_error_model', 'unknown'),
67
+ getattr(ctx, '_llm_last_error_provider', ''),
68
+ getattr(ctx, '_llm_last_error_op_type', 'unknown'),
69
+ url=getattr(ctx, '_llm_last_error_url', ''),
70
+ error_type=last_error_type,
71
+ )
72
+
73
+ @classmethod
74
+ def get_stat(cls, name):
75
+ if cls._instance is None:
76
+ return None
77
+ return cls._instance._stat_map.get(name)
@@ -0,0 +1 @@
1
+ from whatap.llm.log_sink_packs.llm_step_status import LlmStepStatus
@@ -0,0 +1,43 @@
1
+ """LLM 패키지에서 사용하는 상수 및 매핑 데이터 정의."""
2
+ LOG_SINK_CATEGORY = '#LlmCallLog'
3
+
4
+ PROVIDER_TOKEN_FIELDS = {
5
+ 'openai': [
6
+ 'input_tokens', 'output_tokens', 'total_tokens_count',
7
+ 'cached_tokens', 'reasoning_tokens',
8
+ 'audio_input_tokens', 'audio_output_tokens',
9
+ 'accepted_prediction_tokens', 'rejected_prediction_tokens',
10
+ 'embedding_count', 'dimensions', 'similarity',
11
+ ],
12
+ 'anthropic': [
13
+ 'input_tokens', 'output_tokens', 'total_tokens_count',
14
+ 'cache_creation_input_tokens', 'cache_read_input_tokens',
15
+ ],
16
+ }
17
+
18
+ DEFAULT_TOKEN_FIELDS = ['input_tokens', 'output_tokens', 'total_tokens_count']
19
+
20
+ OPENAI_URL_OPERATION_MAP = [
21
+ ("/v1/chat/completions", "chat"),
22
+ ("/v1/responses", "response"),
23
+ ("/v1/completions", "completion"),
24
+ ("/v1/embeddings", "embedding"),
25
+ ("/v1/images/generations", "image_generation"),
26
+ ("/v1/images/edits", "image_edit"),
27
+ ("/v1/audio/transcriptions", "audio_transcription"),
28
+ ("/v1/audio/translations", "audio_translation"),
29
+ ("/v1/audio/speech", "audio_speech"),
30
+ ("/v1/moderations", "moderation"),
31
+ ("/v1/fine_tuning", "fine_tuning"),
32
+ ("/v1/files", "file"),
33
+ ("/v1/assistants", "assistant"),
34
+ ("/v1/threads", "thread"),
35
+ ("/v1/vector_stores", "vector_store"),
36
+ ("/v1/batches", "batch"),
37
+ ]
38
+
39
+ ANTHROPIC_URL_OPERATION_MAP = [
40
+ ("/v1/messages", "chat"),
41
+ ]
42
+
43
+ URL_OPERATION_MAP = OPENAI_URL_OPERATION_MAP + ANTHROPIC_URL_OPERATION_MAP
@@ -0,0 +1,8 @@
1
+ from whatap.llm.log_sink_packs.llm_log_sink_pack import LlmLogSinkPack
2
+ from whatap.llm.log_sink_packs.llm_step_status import LlmStepStatus
3
+ from whatap.llm.log_sink_packs.llm_system_message import LlmSystemMessage
4
+ from whatap.llm.log_sink_packs.llm_input_message import LlmInputMessage
5
+ from whatap.llm.log_sink_packs.llm_output_message import LlmOutputMessage
6
+ from whatap.llm.log_sink_packs.llm_tool_calls import LlmToolCalls
7
+ from whatap.llm.log_sink_packs.llm_tool_results import LlmToolResults
8
+ from whatap.llm.log_sink_packs.llm_tx_status import LlmTxStatus
@@ -0,0 +1,16 @@
1
+ """LLM 입력 메시지 데이터 모델 정의."""
2
+ from whatap.llm.log_sink_packs.llm_log_sink_pack import LlmLogSinkPack
3
+
4
+
5
+ class LlmInputMessage(LlmLogSinkPack):
6
+ """사용자 입력 프롬프트 메시지를 담는 팩."""
7
+
8
+ LLM_LOG_TYPE = 'input_message'
9
+
10
+ def __init__(self):
11
+ super().__init__()
12
+ self.prompt_text = ''
13
+
14
+ def content(self):
15
+ """입력 프롬프트 텍스트를 반환한다."""
16
+ return self.prompt_text