whatap-python 2.0.0rc1__tar.gz → 2.0.2rc1__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 (214) hide show
  1. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/PKG-INFO +5 -1
  2. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/setup.py +6 -0
  3. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/__init__.py +39 -21
  4. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/agent/darwin/amd64/whatap_python +0 -0
  5. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/agent/darwin/arm64/whatap_python +0 -0
  6. whatap_python-2.0.2rc1/whatap/agent/linux/amd64/whatap_python +0 -0
  7. {whatap_python-2.0.0rc1/whatap/agent/linux/amd64 → whatap_python-2.0.2rc1/whatap/agent/linux/arm64}/whatap_python +0 -0
  8. whatap_python-2.0.2rc1/whatap/build.py +4 -0
  9. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/conf/configuration.py +2 -4
  10. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/counter/__init__.py +6 -1
  11. whatap_python-2.0.2rc1/whatap/counter/tasks/__init__.py +3 -0
  12. whatap_python-2.0.2rc1/whatap/counter/tasks/llm_log_sink_task.py +242 -0
  13. whatap_python-2.0.2rc1/whatap/counter/tasks/llm_stat_task.py +77 -0
  14. whatap_python-2.0.2rc1/whatap/llm/__init__.py +1 -0
  15. whatap_python-2.0.2rc1/whatap/llm/definitions.py +43 -0
  16. whatap_python-2.0.2rc1/whatap/llm/log_sink_packs/__init__.py +8 -0
  17. whatap_python-2.0.2rc1/whatap/llm/log_sink_packs/llm_input_message.py +16 -0
  18. whatap_python-2.0.2rc1/whatap/llm/log_sink_packs/llm_log_sink_pack.py +64 -0
  19. whatap_python-2.0.2rc1/whatap/llm/log_sink_packs/llm_output_message.py +19 -0
  20. whatap_python-2.0.2rc1/whatap/llm/log_sink_packs/llm_step_status.py +114 -0
  21. whatap_python-2.0.2rc1/whatap/llm/log_sink_packs/llm_system_message.py +16 -0
  22. whatap_python-2.0.2rc1/whatap/llm/log_sink_packs/llm_tool_calls.py +44 -0
  23. whatap_python-2.0.2rc1/whatap/llm/log_sink_packs/llm_tool_results.py +16 -0
  24. whatap_python-2.0.2rc1/whatap/llm/log_sink_packs/llm_tx_status.py +102 -0
  25. whatap_python-2.0.2rc1/whatap/llm/pricing.py +236 -0
  26. whatap_python-2.0.2rc1/whatap/llm/providers/anthropic/__init__.py +37 -0
  27. whatap_python-2.0.2rc1/whatap/llm/providers/anthropic/messages/messages.py +67 -0
  28. whatap_python-2.0.2rc1/whatap/llm/providers/anthropic/messages/messages_context.py +76 -0
  29. whatap_python-2.0.2rc1/whatap/llm/providers/anthropic/messages/messages_extractor.py +123 -0
  30. whatap_python-2.0.2rc1/whatap/llm/providers/interceptor.py +85 -0
  31. {whatap_python-2.0.0rc1/whatap/trace/mod/llm → whatap_python-2.0.2rc1/whatap/llm/providers}/openai/__init__.py +33 -4
  32. whatap_python-2.0.2rc1/whatap/llm/providers/openai/chat/chat.py +79 -0
  33. whatap_python-2.0.2rc1/whatap/llm/providers/openai/chat/chat_context.py +78 -0
  34. whatap_python-2.0.2rc1/whatap/llm/providers/openai/chat/chat_extractor.py +126 -0
  35. whatap_python-2.0.2rc1/whatap/llm/providers/openai/completions/completions.py +67 -0
  36. whatap_python-2.0.2rc1/whatap/llm/providers/openai/completions/completions_context.py +32 -0
  37. whatap_python-2.0.2rc1/whatap/llm/providers/openai/completions/completions_extractor.py +61 -0
  38. whatap_python-2.0.0rc1/whatap/trace/mod/llm/openai/util.py → whatap_python-2.0.2rc1/whatap/llm/providers/openai/content_parser.py +9 -1
  39. whatap_python-2.0.2rc1/whatap/llm/providers/openai/embeddings/embeddings.py +54 -0
  40. whatap_python-2.0.2rc1/whatap/llm/providers/openai/embeddings/embeddings_context.py +26 -0
  41. whatap_python-2.0.2rc1/whatap/llm/providers/openai/embeddings/embeddings_extractor.py +26 -0
  42. whatap_python-2.0.2rc1/whatap/llm/providers/openai/responses/responses.py +67 -0
  43. whatap_python-2.0.2rc1/whatap/llm/providers/openai/responses/responses_context.py +88 -0
  44. whatap_python-2.0.2rc1/whatap/llm/providers/openai/responses/responses_extractor.py +125 -0
  45. whatap_python-2.0.2rc1/whatap/llm/providers/stream_accumulator.py +73 -0
  46. whatap_python-2.0.2rc1/whatap/llm/stats/__init__.py +15 -0
  47. whatap_python-2.0.0rc1/whatap/counter/tasks/llm_active_stat.py → whatap_python-2.0.2rc1/whatap/llm/stats/active_stat.py +21 -20
  48. whatap_python-2.0.2rc1/whatap/llm/stats/api_status_stat.py +28 -0
  49. whatap_python-2.0.0rc1/whatap/counter/tasks/llm_api_status_stat.py → whatap_python-2.0.2rc1/whatap/llm/stats/base_stat.py +20 -24
  50. whatap_python-2.0.2rc1/whatap/llm/stats/error_stat.py +56 -0
  51. whatap_python-2.0.2rc1/whatap/llm/stats/feature_stat.py +63 -0
  52. whatap_python-2.0.2rc1/whatap/llm/stats/meter.py +18 -0
  53. whatap_python-2.0.2rc1/whatap/llm/stats/perf_stat.py +105 -0
  54. whatap_python-2.0.2rc1/whatap/llm/stats/token_usage_stat.py +104 -0
  55. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/net/udp_session.py +29 -14
  56. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/net/udp_thread.py +2 -4
  57. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/scripts/__init__.py +6 -6
  58. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/wsgi.py +4 -1
  59. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/database/util.py +3 -11
  60. whatap_python-2.0.2rc1/whatap/trace/mod/email/__init__.py +0 -0
  61. whatap_python-2.0.2rc1/whatap/trace/mod/httpc/__init__.py +0 -0
  62. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/httpc/util.py +51 -33
  63. whatap_python-2.0.2rc1/whatap/trace/mod/standalone/__init__.py +0 -0
  64. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/standalone/multiple.py +4 -1
  65. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/standalone/single.py +4 -1
  66. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/trace_context.py +1 -0
  67. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/trace_context_manager.py +40 -1
  68. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/trace_module_definition.py +2 -2
  69. whatap_python-2.0.2rc1/whatap/util/__init__.py +0 -0
  70. whatap_python-2.0.2rc1/whatap/util/cardinality/__init__.py +0 -0
  71. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap_python.egg-info/PKG-INFO +5 -1
  72. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap_python.egg-info/SOURCES.txt +49 -16
  73. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap_python.egg-info/requires.txt +4 -0
  74. whatap_python-2.0.0rc1/whatap/agent/linux/arm64/whatap_python +0 -0
  75. whatap_python-2.0.0rc1/whatap/build.py +0 -4
  76. whatap_python-2.0.0rc1/whatap/counter/tasks/__init__.py +0 -17
  77. whatap_python-2.0.0rc1/whatap/counter/tasks/llm_error_stat.py +0 -88
  78. whatap_python-2.0.0rc1/whatap/counter/tasks/llm_feature_stat.py +0 -91
  79. whatap_python-2.0.0rc1/whatap/counter/tasks/llm_meter.py +0 -57
  80. whatap_python-2.0.0rc1/whatap/counter/tasks/llm_perf_stat.py +0 -132
  81. whatap_python-2.0.0rc1/whatap/counter/tasks/llm_token_usage_stat.py +0 -122
  82. whatap_python-2.0.0rc1/whatap/trace/mod/llm/anthropic.py +0 -488
  83. whatap_python-2.0.0rc1/whatap/trace/mod/llm/llm_log_sink_pack.py +0 -316
  84. whatap_python-2.0.0rc1/whatap/trace/mod/llm/llm_sender.py +0 -527
  85. whatap_python-2.0.0rc1/whatap/trace/mod/llm/openai/chat.py +0 -356
  86. whatap_python-2.0.0rc1/whatap/trace/mod/llm/openai/embeddings.py +0 -178
  87. whatap_python-2.0.0rc1/whatap/trace/mod/llm/openai/responses.py +0 -409
  88. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/README.md +0 -0
  89. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/pyproject.toml +0 -0
  90. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/setup.cfg +0 -0
  91. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/LICENSE +0 -0
  92. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/README.rst +0 -0
  93. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/__main__.py +0 -0
  94. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/agent/windows/whatap_python.exe +0 -0
  95. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/bootstrap/__init__.py +0 -0
  96. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/bootstrap/sitecustomize.py +0 -0
  97. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/conf/__init__.py +0 -0
  98. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/conf/configure.py +0 -0
  99. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/conf/license.py +0 -0
  100. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/control/__init__.py +0 -0
  101. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/counter/counter_manager.py +0 -0
  102. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/counter/tasks/base_task.py +0 -0
  103. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/counter/tasks/openfiledescriptor.py +0 -0
  104. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/io/__init__.py +0 -0
  105. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/io/data_inputx.py +0 -0
  106. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/io/data_outputx.py +0 -0
  107. {whatap_python-2.0.0rc1/whatap/net → whatap_python-2.0.2rc1/whatap/llm/providers}/__init__.py +0 -0
  108. {whatap_python-2.0.0rc1/whatap/pack → whatap_python-2.0.2rc1/whatap/llm/providers/anthropic/messages}/__init__.py +0 -0
  109. {whatap_python-2.0.0rc1/whatap/trace/mod → whatap_python-2.0.2rc1/whatap/llm/providers/openai/chat}/__init__.py +0 -0
  110. {whatap_python-2.0.0rc1/whatap/trace/mod/amqp → whatap_python-2.0.2rc1/whatap/llm/providers/openai/completions}/__init__.py +0 -0
  111. {whatap_python-2.0.0rc1/whatap/trace/mod/application → whatap_python-2.0.2rc1/whatap/llm/providers/openai/embeddings}/__init__.py +0 -0
  112. {whatap_python-2.0.0rc1/whatap/trace/mod/database → whatap_python-2.0.2rc1/whatap/llm/providers/openai/responses}/__init__.py +0 -0
  113. {whatap_python-2.0.0rc1/whatap/trace/mod/email → whatap_python-2.0.2rc1/whatap/net}/__init__.py +0 -0
  114. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/net/async_sender.py +0 -0
  115. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/net/packet_enum.py +0 -0
  116. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/net/packet_type_enum.py +0 -0
  117. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/net/param_def.py +0 -0
  118. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/net/stackhelper.py +0 -0
  119. {whatap_python-2.0.0rc1/whatap/trace/mod/httpc → whatap_python-2.0.2rc1/whatap/pack}/__init__.py +0 -0
  120. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/pack/logSinkPack.py +0 -0
  121. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/pack/pack.py +0 -0
  122. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/pack/pack_enum.py +0 -0
  123. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/pack/tagCountPack.py +0 -0
  124. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/__init__.py +0 -0
  125. {whatap_python-2.0.0rc1/whatap/trace/mod/llm → whatap_python-2.0.2rc1/whatap/trace/mod}/__init__.py +0 -0
  126. {whatap_python-2.0.0rc1/whatap/trace/mod/standalone → whatap_python-2.0.2rc1/whatap/trace/mod/amqp}/__init__.py +0 -0
  127. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/amqp/kombu.py +0 -0
  128. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/amqp/pika.py +0 -0
  129. {whatap_python-2.0.0rc1/whatap/util → whatap_python-2.0.2rc1/whatap/trace/mod/application}/__init__.py +0 -0
  130. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/bottle.py +0 -0
  131. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/celery.py +0 -0
  132. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/cherrypy.py +0 -0
  133. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/django.py +0 -0
  134. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/django_asgi.py +0 -0
  135. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/django_py3.py +0 -0
  136. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/fastapi.py +0 -0
  137. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/flask.py +0 -0
  138. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/frappe.py +0 -0
  139. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/graphql.py +0 -0
  140. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/nameko.py +0 -0
  141. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/odoo.py +0 -0
  142. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/starlette.py +0 -0
  143. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/tornado.py +0 -0
  144. {whatap_python-2.0.0rc1/whatap/util/cardinality → whatap_python-2.0.2rc1/whatap/trace/mod/database}/__init__.py +0 -0
  145. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/database/cxoracle.py +0 -0
  146. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/database/mongo.py +0 -0
  147. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/database/mysql.py +0 -0
  148. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/database/neo4j.py +0 -0
  149. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/database/psycopg2.py +0 -0
  150. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/database/psycopg3.py +0 -0
  151. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/database/redis.py +0 -0
  152. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/database/sqlalchemy.py +0 -0
  153. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/database/sqlite3.py +0 -0
  154. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/email/smtp.py +0 -0
  155. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/httpc/django.py +0 -0
  156. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/httpc/httplib.py +0 -0
  157. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/httpc/httpx.py +0 -0
  158. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/httpc/requests.py +0 -0
  159. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/httpc/urllib3.py +0 -0
  160. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/logging.py +0 -0
  161. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/plugin.py +0 -0
  162. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/simple_trace_context.py +0 -0
  163. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/trace_error.py +0 -0
  164. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/trace_handler.py +0 -0
  165. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/trace_import.py +0 -0
  166. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/bit_util.py +0 -0
  167. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/cardinality/hyperloglog.py +0 -0
  168. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/cardinality/murmurhash.py +0 -0
  169. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/cardinality/registerset.py +0 -0
  170. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/compare_util.py +0 -0
  171. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/date_util.py +0 -0
  172. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/debug_util.py +0 -0
  173. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/escape_literal_sql.py +0 -0
  174. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/frame_util.py +0 -0
  175. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/hash_util.py +0 -0
  176. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/hexa32.py +0 -0
  177. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/int_set.py +0 -0
  178. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/ip_util.py +0 -0
  179. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/keygen.py +0 -0
  180. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/linked_list.py +0 -0
  181. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/linked_map.py +0 -0
  182. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/metering_util.py +0 -0
  183. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/request_double_queue.py +0 -0
  184. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/request_queue.py +0 -0
  185. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/string_util.py +0 -0
  186. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/throttle_util.py +0 -0
  187. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/userid_util.py +0 -0
  188. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/__init__.py +0 -0
  189. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/blob_value.py +0 -0
  190. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/boolean_value.py +0 -0
  191. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/decimal_value.py +0 -0
  192. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/double_summary.py +0 -0
  193. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/double_value.py +0 -0
  194. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/float_array.py +0 -0
  195. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/float_value.py +0 -0
  196. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/int_array.py +0 -0
  197. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/ip4_value.py +0 -0
  198. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/list_value.py +0 -0
  199. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/long_array.py +0 -0
  200. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/long_summary.py +0 -0
  201. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/map_value.py +0 -0
  202. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/null_value.py +0 -0
  203. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/number_value.py +0 -0
  204. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/summary_value.py +0 -0
  205. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/text_array.py +0 -0
  206. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/text_hash_value.py +0 -0
  207. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/text_value.py +0 -0
  208. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/value.py +0 -0
  209. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/value_enum.py +0 -0
  210. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/whatap.conf +0 -0
  211. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap_python.egg-info/dependency_links.txt +0 -0
  212. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap_python.egg-info/entry_points.txt +0 -0
  213. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap_python.egg-info/not-zip-safe +0 -0
  214. {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/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.2rc1
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'
@@ -690,8 +688,10 @@ except ImportError:
690
688
  if sys.platform == 'win32':
691
689
  import tempfile
692
690
  default_lock_file = os.path.join(tempfile.gettempdir(), 'whatap-python.lock')
691
+ default_llm_lock_file = os.path.join(tempfile.gettempdir(), 'whatap-python-llm.lock')
693
692
  else:
694
693
  default_lock_file = '/tmp/whatap-python.lock'
694
+ default_llm_lock_file = '/tmp/whatap-python-llm.lock'
695
695
 
696
696
  def openPortFile(filepath=os.environ.get('WHATAP_LOCK_FILE', default_lock_file)):
697
697
  f = None
@@ -732,12 +732,12 @@ def openPortFile(filepath=os.environ.get('WHATAP_LOCK_FILE', default_lock_file))
732
732
  whatap_print('WHATAP: file close failed: %s' % e)
733
733
  return None
734
734
 
735
- def get_port_number(port=6600, home=os.environ.get('WHATAP_HOME')):
735
+ def get_port_number(port=6600, home=os.environ.get('WHATAP_HOME'), lock_file=None):
736
736
  if not home:
737
737
  return None
738
738
 
739
739
  for i in range(100):
740
- f = openPortFile()
740
+ f = openPortFile(filepath=lock_file) if lock_file else openPortFile()
741
741
  if not f:
742
742
  if i > 50:
743
743
  time.sleep(0.1)
@@ -798,6 +798,24 @@ def configPort():
798
798
  return port
799
799
 
800
800
 
801
+ def configLlmPort():
802
+ # force_llm_net_udp_port가 설정된 경우 lock 파일 무시하고 해당 포트 강제 사용
803
+ force_port_str = preview_whatap_conf("force_llm_net_udp_port")
804
+ if force_port_str and force_port_str not in ('false', 'False'):
805
+ try:
806
+ port = int(force_port_str)
807
+ except ValueError:
808
+ whatap_print('WHATAP: force_llm_net_udp_port value is invalid: {}'.format(force_port_str))
809
+ port = None
810
+ else:
811
+ llm_lock_file = os.environ.get('WHATAP_LLM_LOCK_FILE', default_llm_lock_file)
812
+ port = get_port_number(port=6700, home=os.environ.get('WHATAP_HOME'), lock_file=llm_lock_file)
813
+
814
+ if port:
815
+ update_config('WHATAP_HOME', 'llm_net_udp_port', str(port))
816
+ return port
817
+
818
+
801
819
  def find_whatap_conf():
802
820
  # 1. 현재 디렉토리 검색
803
821
  script_dir = os.path.dirname(os.path.abspath(__file__))
@@ -0,0 +1,4 @@
1
+ app = 'Python'
2
+ name = 'whatap-python'
3
+ version = '2.0.2rc1'
4
+ release_date = '20260414'
@@ -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,242 @@
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
+ except Exception as e:
86
+ logging.warning('[LLM] send_llm_tx_status failed: %s' % e, extra={'id': 'LLM022'})
87
+
88
+ # ── internal ──
89
+
90
+ def _ensure_started(self):
91
+ if self._started:
92
+ return
93
+ with self._lock:
94
+ if self._started:
95
+ return
96
+ self._started = True
97
+ t = threading.Thread(target=self._run, daemon=True)
98
+ t.start()
99
+
100
+ def _run(self):
101
+ while True:
102
+ pack = self._q.get()
103
+ if not pack:
104
+ continue
105
+ try:
106
+ self._process_pack(pack)
107
+ except Exception as e:
108
+ logging.warning('[LLM] process failed: %s' % e, extra={'id': 'LLM003'})
109
+
110
+ def _process_pack(self, pack):
111
+ # llm_step_status
112
+ self._send_log_sink(pack)
113
+
114
+ # system_message
115
+ for text in getattr(pack, 'system_texts', None) or []:
116
+ self._send_log_sink(LlmSystemMessage.from_pack(pack, system_text=text))
117
+
118
+ # input_message
119
+ prompt_text = getattr(pack, 'prompt_text', '') or ''
120
+ self._send_log_sink(LlmInputMessage.from_pack(pack, prompt_text=prompt_text))
121
+
122
+ # output_message
123
+ completion_text = getattr(pack, 'completion_text', '') or ''
124
+ reasoning_text = getattr(pack, 'reasoning_text', '') or ''
125
+ self._send_log_sink(LlmOutputMessage.from_pack(pack,
126
+ completion_text=completion_text, reasoning_text=reasoning_text))
127
+
128
+ # tool
129
+ tool_calls_text = getattr(pack, 'tool_calls_text', '') or ''
130
+ if tool_calls_text:
131
+ self._send_log_sink(LlmToolCalls.from_pack(pack, tool_calls_text=tool_calls_text))
132
+
133
+ # tool_result
134
+ tool_results_text = getattr(pack, 'tool_results_text', '') or ''
135
+ if tool_results_text:
136
+ self._send_log_sink(LlmToolResults.from_pack(pack, tool_results_text=tool_results_text))
137
+
138
+ def _accumulate_tx_summary(self, ctx, pack):
139
+ if not ctx:
140
+ return
141
+ tx = getattr(ctx, '_llm_tx_status', None)
142
+ if tx is None:
143
+ tx = LlmTxStatus()
144
+ tx.txid = str(ctx.id)
145
+ ctx._llm_tx_status = tx
146
+ tx.accumulate(pack)
147
+
148
+ def _send_log_sink(self, data):
149
+ tags = data.tags()
150
+ fields = data.fields()
151
+ content = data.content()
152
+ if content:
153
+ self._send_chunked(tags, fields, content, data.index)
154
+ else:
155
+ self._send_pack(tags, fields, '', data.index)
156
+
157
+ def _send_pack(self, tags, fields, content, index):
158
+ try:
159
+ tags = {k: v for k, v in tags.items() if v is not None}
160
+ fields = {k: v for k, v in fields.items() if v is not None}
161
+ fields['index'] = index
162
+ if content:
163
+ encoded = content.encode('utf-8', errors='replace')
164
+ if len(encoded) > _MAX_CONTENT_BYTES:
165
+ content = encoded[:_MAX_CONTENT_BYTES].decode('utf-8', errors='replace')
166
+ p = logSinkPack.getLogSinkPack(
167
+ t=DateUtil.now(),
168
+ category=LlmLogSinkPack.CATEGORY,
169
+ tags=tags,
170
+ fields={},
171
+ line=DateUtil.now(),
172
+ content=content
173
+ )
174
+ for k, v in fields.items():
175
+ if isinstance(v, (int, float)):
176
+ p.fields.putAuto(k, v)
177
+ else:
178
+ p.fields.putString(k, str(v))
179
+
180
+ p.pcode = conf.PCODE
181
+ bout = whatapio.DataOutputX()
182
+ bout.writePack(p, None)
183
+ packbytes = bout.toByteArray()
184
+ async_sender.send_llm_relaypack(packbytes)
185
+ except Exception as e:
186
+ logging.warning('[LLM] send_pack failed: %s' % e, extra={'id': 'LLM001'})
187
+
188
+ def _send_chunked(self, tags, fields, content, index):
189
+ try:
190
+ if not content:
191
+ self._send_pack(tags, fields, '', index)
192
+ return
193
+
194
+ tags = {k: v for k, v in tags.items() if v is not None}
195
+ fields = {k: v for k, v in fields.items() if v is not None}
196
+
197
+ content_bytes = content.encode('utf-8', errors='replace')
198
+ total_bytes = len(content_bytes)
199
+
200
+ if total_bytes <= _MAX_CONTENT_BYTES:
201
+ chunk_fields = dict(fields)
202
+ chunk_fields["chunk_index"] = 0
203
+ chunk_fields["chunk_total"] = 1
204
+ self._send_pack(tags, chunk_fields, content, index)
205
+ return
206
+
207
+ raw_chunks = []
208
+ i = 0
209
+ while i < total_bytes:
210
+ end = min(i + _MAX_CONTENT_BYTES, total_bytes)
211
+ if end < total_bytes:
212
+ while end > i and (content_bytes[end] & 0xC0) == 0x80:
213
+ end -= 1
214
+ raw_chunks.append(content_bytes[i:end].decode('utf-8', errors='replace'))
215
+ i = end
216
+
217
+ chunk_total = len(raw_chunks)
218
+ for idx, chunk in enumerate(raw_chunks):
219
+ chunk_fields = dict(fields)
220
+ chunk_fields["chunk_index"] = idx
221
+ chunk_fields["chunk_total"] = chunk_total
222
+ self._send_pack(tags, chunk_fields, chunk, index)
223
+ except Exception as e:
224
+ logging.warning('[LLM] send_chunked failed: %s' % e, extra={'id': 'LLM002'})
225
+
226
+
227
+ # ── module-level API ──
228
+
229
+ def dispatch_llm_pack(pack):
230
+ LlmLogSinkTask.get_instance().dispatch(pack)
231
+
232
+
233
+ def send_llm_tx_status(ctx):
234
+ LlmLogSinkTask.get_instance().send_tx_status(ctx)
235
+
236
+
237
+ def send_llm_pack(metadata):
238
+ pack = LlmStepStatus()
239
+ for key, val in metadata.items():
240
+ if hasattr(pack, key):
241
+ setattr(pack, key, val)
242
+ 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
@@ -0,0 +1,64 @@
1
+ """LLM LogSink 팩의 기본 클래스 정의."""
2
+ from whatap.llm.definitions import LOG_SINK_CATEGORY
3
+
4
+
5
+ class LlmLogSinkPack(object):
6
+ """LLM LogSink 팩 기본 클래스로, 공통 태그와 컨텍스트 설정을 제공한다."""
7
+
8
+ CATEGORY = LOG_SINK_CATEGORY
9
+ LLM_LOG_TYPE = None
10
+
11
+ def __init__(self):
12
+ """공통 필드(txid, step_id, index, provider, url, operation_type)를 초기화한다."""
13
+ self.txid = None
14
+ self.step_id = None
15
+ self.index = 0
16
+ self.provider = ''
17
+ self.url = ''
18
+ self.operation_type = 'unknown'
19
+
20
+ @classmethod
21
+ def from_pack(cls, pack, **kwargs):
22
+ """기존 팩으로부터 새 인스턴스를 생성한다."""
23
+ obj = cls()
24
+ obj.txid = pack.txid
25
+ obj.step_id = pack.step_id
26
+ obj.provider = pack.provider
27
+ obj.index = pack.index
28
+ for key, val in kwargs.items():
29
+ setattr(obj, key, val)
30
+ return obj
31
+
32
+ def set_context(self, ctx):
33
+ """트랜잭션 컨텍스트에서 txid, step_id, provider 정보를 설정한다."""
34
+ if ctx:
35
+ if not self.txid:
36
+ self.txid = str(ctx.id)
37
+ if not self.step_id:
38
+ self.step_id = str(getattr(ctx, '_llm_step_id', 0))
39
+ if not self.provider:
40
+ httpc_url = getattr(ctx, '_llm_httpc_url', '') or ''
41
+ if httpc_url:
42
+ url = httpc_url
43
+ if '://' in url:
44
+ url = url.split('://', 1)[1]
45
+ self.provider = url.split('/', 1)[0]
46
+ if '/' in url:
47
+ self.url = '/' + url.split('/', 1)[1]
48
+
49
+ def tags(self):
50
+ """공통 태그 딕셔너리를 반환한다."""
51
+ return {
52
+ 'llm_log_type': self.LLM_LOG_TYPE,
53
+ '@txid': self.txid,
54
+ '@step_id': self.step_id,
55
+ 'provider': self.provider,
56
+ }
57
+
58
+ def fields(self):
59
+ """필드 딕셔너리를 반환한다."""
60
+ return {}
61
+
62
+ def content(self):
63
+ """로그 콘텐츠 문자열을 반환한다."""
64
+ return ''