whatap-python 2.1.4__tar.gz → 2.2.0__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 (289) hide show
  1. {whatap_python-2.1.4 → whatap_python-2.2.0}/PKG-INFO +1 -1
  2. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/__init__.py +222 -102
  3. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/agent/linux/amd64/whatap_python +0 -0
  4. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/agent/linux/arm64/whatap_python +0 -0
  5. whatap_python-2.2.0/whatap/build.py +4 -0
  6. whatap_python-2.2.0/whatap/conf/configuration.py +444 -0
  7. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/conf/configure.py +55 -0
  8. whatap_python-2.2.0/whatap/counter/__init__.py +36 -0
  9. whatap_python-2.2.0/whatap/counter/tasks/base_task.py +35 -0
  10. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/counter/tasks/llm_evaluator_task.py +56 -10
  11. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/counter/tasks/llm_log_sink_task.py +85 -19
  12. whatap_python-2.2.0/whatap/counter/tasks/llm_stat_task.py +138 -0
  13. whatap_python-2.2.0/whatap/llm/__init__.py +70 -0
  14. whatap_python-2.2.0/whatap/llm/api/__init__.py +20 -0
  15. whatap_python-2.2.0/whatap/llm/api/agent_name.py +83 -0
  16. {whatap_python-2.1.4/whatap/llm → whatap_python-2.2.0/whatap/llm/api}/prompt_meta.py +31 -4
  17. whatap_python-2.2.0/whatap/llm/api/session.py +175 -0
  18. whatap_python-2.2.0/whatap/llm/api/turn.py +88 -0
  19. whatap_python-2.2.0/whatap/llm/api/user.py +176 -0
  20. whatap_python-2.2.0/whatap/llm/api/workflow.py +115 -0
  21. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/evaluators/__init__.py +4 -0
  22. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/evaluators/base.py +15 -11
  23. whatap_python-2.2.0/whatap/llm/evaluators/builtins/_json_repair.py +106 -0
  24. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/evaluators/builtins/combined_judge.py +7 -4
  25. whatap_python-2.2.0/whatap/llm/evaluators/builtins/judge_factories.py +90 -0
  26. whatap_python-2.2.0/whatap/llm/evaluators/builtins/llm_judge.py +248 -0
  27. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/evaluators/builtins/pii_leak.py +5 -20
  28. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/evaluators/builtins/url_scan.py +21 -22
  29. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/evaluators/registry.py +91 -0
  30. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/evaluators/scope.py +22 -2
  31. whatap_python-2.2.0/whatap/llm/instrument/__init__.py +32 -0
  32. whatap_python-2.2.0/whatap/llm/instrument/_boundary.py +492 -0
  33. whatap_python-2.2.0/whatap/llm/instrument/agent.py +35 -0
  34. whatap_python-2.2.0/whatap/llm/instrument/llm_api.py +89 -0
  35. whatap_python-2.2.0/whatap/llm/instrument/workflow.py +42 -0
  36. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/log_sink_packs/llm_log_sink_pack.py +32 -0
  37. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/log_sink_packs/llm_tx_status.py +51 -0
  38. whatap_python-2.2.0/whatap/llm/orchestration/__init__.py +16 -0
  39. whatap_python-2.2.0/whatap/llm/orchestration/hermes/__init__.py +143 -0
  40. whatap_python-2.2.0/whatap/llm/orchestration/hermes/gateway.py +172 -0
  41. whatap_python-2.2.0/whatap/llm/orchestration/hermes/kanban.py +353 -0
  42. whatap_python-2.2.0/whatap/llm/orchestration/langgraph/__init__.py +134 -0
  43. whatap_python-2.2.0/whatap/llm/pricing.py +187 -0
  44. whatap_python-2.2.0/whatap/llm/providers/anthropic/__init__.py +72 -0
  45. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/anthropic/messages/messages_context.py +48 -0
  46. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/anthropic/messages/messages_extractor.py +53 -0
  47. whatap_python-2.2.0/whatap/llm/providers/anthropic/messages/messages_stream.py +162 -0
  48. whatap_python-2.2.0/whatap/llm/providers/interceptor.py +349 -0
  49. whatap_python-2.2.0/whatap/llm/providers/openai/__init__.py +110 -0
  50. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/openai/chat/chat_context.py +6 -0
  51. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/openai/content_parser.py +4 -1
  52. whatap_python-2.2.0/whatap/llm/providers/stream_accumulator.py +287 -0
  53. whatap_python-2.2.0/whatap/llm/providers/workflow.py +681 -0
  54. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/stats/__init__.py +6 -0
  55. whatap_python-2.2.0/whatap/llm/stats/active_agent_stat.py +93 -0
  56. whatap_python-2.2.0/whatap/llm/stats/active_stat.py +83 -0
  57. whatap_python-2.2.0/whatap/llm/stats/actx_stat.py +80 -0
  58. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/stats/base_stat.py +6 -0
  59. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/stats/eval_stat.py +1 -0
  60. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/stats/pii_leak_eval_stat.py +2 -1
  61. whatap_python-2.2.0/whatap/llm/stats/traj_actx_stat.py +84 -0
  62. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/stats/url_scan_eval_stat.py +2 -1
  63. whatap_python-2.2.0/whatap/net/async_sender.py +313 -0
  64. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/net/packet_enum.py +1 -1
  65. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/net/param_def.py +11 -0
  66. whatap_python-2.2.0/whatap/net/udp_session.py +790 -0
  67. whatap_python-2.2.0/whatap/net/udp_thread.py +103 -0
  68. whatap_python-2.2.0/whatap/trace/mod/application/fastapi/instrumentation.py +403 -0
  69. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/application/fastapi/scope.py +17 -0
  70. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/application/wsgi.py +19 -1
  71. whatap_python-2.2.0/whatap/trace/mod/hook_service_patterns.py +26 -0
  72. whatap_python-2.2.0/whatap/trace/mod/httpc/util.py +689 -0
  73. whatap_python-2.2.0/whatap/trace/mod/pattern_targets.py +137 -0
  74. whatap_python-2.2.0/whatap/trace/mod/plugin.py +47 -0
  75. whatap_python-2.2.0/whatap/trace/mod/process/subprocess_spawn.py +390 -0
  76. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/standalone/multiple.py +2 -1
  77. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/standalone/single.py +2 -1
  78. whatap_python-2.2.0/whatap/trace/mod/thread_context.py +202 -0
  79. whatap_python-2.2.0/whatap/trace/mod/workflow_plugin.py +33 -0
  80. whatap_python-2.2.0/whatap/trace/mtrace_env.py +368 -0
  81. whatap_python-2.2.0/whatap/trace/patch.py +98 -0
  82. whatap_python-2.2.0/whatap/trace/trace_context.py +330 -0
  83. whatap_python-2.2.0/whatap/trace/trace_context_manager.py +1005 -0
  84. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/trace_error.py +8 -2
  85. whatap_python-2.2.0/whatap/trace/trace_import.py +136 -0
  86. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/trace_module_definition.py +28 -0
  87. whatap_python-2.2.0/whatap/util/cardinality/__init__.py +0 -0
  88. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap_python.egg-info/PKG-INFO +1 -1
  89. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap_python.egg-info/SOURCES.txt +31 -3
  90. whatap_python-2.1.4/tests/test_stream_ttft.py +0 -192
  91. whatap_python-2.1.4/whatap/build.py +0 -4
  92. whatap_python-2.1.4/whatap/conf/configuration.py +0 -284
  93. whatap_python-2.1.4/whatap/counter/__init__.py +0 -14
  94. whatap_python-2.1.4/whatap/counter/tasks/base_task.py +0 -26
  95. whatap_python-2.1.4/whatap/counter/tasks/llm_stat_task.py +0 -78
  96. whatap_python-2.1.4/whatap/llm/__init__.py +0 -17
  97. whatap_python-2.1.4/whatap/llm/evaluators/builtins/llm_judge.py +0 -516
  98. whatap_python-2.1.4/whatap/llm/pricing.py +0 -236
  99. whatap_python-2.1.4/whatap/llm/providers/anthropic/__init__.py +0 -37
  100. whatap_python-2.1.4/whatap/llm/providers/interceptor.py +0 -232
  101. whatap_python-2.1.4/whatap/llm/providers/openai/__init__.py +0 -133
  102. whatap_python-2.1.4/whatap/llm/providers/stream_accumulator.py +0 -139
  103. whatap_python-2.1.4/whatap/llm/stats/active_stat.py +0 -86
  104. whatap_python-2.1.4/whatap/llm/stats/meter.py +0 -18
  105. whatap_python-2.1.4/whatap/net/async_sender.py +0 -107
  106. whatap_python-2.1.4/whatap/net/udp_session.py +0 -396
  107. whatap_python-2.1.4/whatap/net/udp_thread.py +0 -54
  108. whatap_python-2.1.4/whatap/trace/mod/application/fastapi/instrumentation.py +0 -204
  109. whatap_python-2.1.4/whatap/trace/mod/hook_service_patterns.py +0 -72
  110. whatap_python-2.1.4/whatap/trace/mod/httpc/util.py +0 -388
  111. whatap_python-2.1.4/whatap/trace/mod/plugin.py +0 -84
  112. whatap_python-2.1.4/whatap/trace/trace_context.py +0 -212
  113. whatap_python-2.1.4/whatap/trace/trace_context_manager.py +0 -255
  114. whatap_python-2.1.4/whatap/trace/trace_import.py +0 -99
  115. {whatap_python-2.1.4 → whatap_python-2.2.0}/README.md +0 -0
  116. {whatap_python-2.1.4 → whatap_python-2.2.0}/pyproject.toml +0 -0
  117. {whatap_python-2.1.4 → whatap_python-2.2.0}/setup.cfg +0 -0
  118. {whatap_python-2.1.4 → whatap_python-2.2.0}/setup.py +0 -0
  119. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/LICENSE +0 -0
  120. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/README.rst +0 -0
  121. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/__main__.py +0 -0
  122. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/agent/darwin/amd64/whatap_python +0 -0
  123. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/agent/darwin/arm64/whatap_python +0 -0
  124. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/agent/windows/whatap_python.exe +0 -0
  125. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/bootstrap/__init__.py +0 -0
  126. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/bootstrap/sitecustomize.py +0 -0
  127. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/conf/__init__.py +0 -0
  128. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/conf/license.py +0 -0
  129. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/control/__init__.py +0 -0
  130. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/counter/counter_manager.py +0 -0
  131. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/counter/tasks/__init__.py +0 -0
  132. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/counter/tasks/openfiledescriptor.py +0 -0
  133. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/io/__init__.py +0 -0
  134. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/io/data_inputx.py +0 -0
  135. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/io/data_outputx.py +0 -0
  136. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/definitions.py +0 -0
  137. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/evaluators/builtins/__init__.py +0 -0
  138. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/evaluators/builtins/answer_relevance.py +0 -0
  139. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/evaluators/builtins/factuality.py +0 -0
  140. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/evaluators/builtins/hallucination.py +0 -0
  141. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/evaluators/builtins/prompt_injection.py +0 -0
  142. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/evaluators/builtins/toxicity.py +0 -0
  143. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/evaluators/sampler.py +0 -0
  144. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/features.py +0 -0
  145. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/log_sink_packs/__init__.py +0 -0
  146. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/log_sink_packs/llm_input_message.py +0 -0
  147. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/log_sink_packs/llm_output_message.py +0 -0
  148. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/log_sink_packs/llm_step_eval_status.py +0 -0
  149. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/log_sink_packs/llm_step_status.py +0 -0
  150. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/log_sink_packs/llm_system_message.py +0 -0
  151. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/log_sink_packs/llm_tool_calls.py +0 -0
  152. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/log_sink_packs/llm_tool_results.py +0 -0
  153. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/__init__.py +0 -0
  154. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/anthropic/messages/__init__.py +0 -0
  155. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/anthropic/messages/messages.py +0 -0
  156. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/openai/chat/__init__.py +0 -0
  157. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/openai/chat/chat.py +0 -0
  158. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/openai/chat/chat_extractor.py +0 -0
  159. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/openai/completions/__init__.py +0 -0
  160. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/openai/completions/completions.py +0 -0
  161. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/openai/completions/completions_context.py +0 -0
  162. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/openai/completions/completions_extractor.py +0 -0
  163. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/openai/embeddings/__init__.py +0 -0
  164. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/openai/embeddings/embeddings.py +0 -0
  165. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/openai/embeddings/embeddings_context.py +0 -0
  166. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/openai/embeddings/embeddings_extractor.py +0 -0
  167. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/openai/responses/__init__.py +0 -0
  168. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/openai/responses/responses.py +0 -0
  169. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/openai/responses/responses_context.py +0 -0
  170. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/providers/openai/responses/responses_extractor.py +0 -0
  171. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/stats/answer_relevance_eval_stat.py +0 -0
  172. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/stats/api_status_stat.py +0 -0
  173. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/stats/combined_judge_eval_stat.py +0 -0
  174. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/stats/error_stat.py +0 -0
  175. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/stats/factuality_eval_stat.py +0 -0
  176. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/stats/feature_stat.py +0 -0
  177. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/stats/finish_stat.py +0 -0
  178. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/stats/hallucination_eval_stat.py +0 -0
  179. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/stats/perf_stat.py +0 -0
  180. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/stats/prompt_injection_eval_stat.py +0 -0
  181. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/stats/token_usage_stat.py +0 -0
  182. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/llm/stats/toxicity_eval_stat.py +0 -0
  183. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/net/__init__.py +0 -0
  184. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/net/packet_type_enum.py +0 -0
  185. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/net/stackhelper.py +0 -0
  186. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/pack/__init__.py +0 -0
  187. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/pack/logSinkPack.py +0 -0
  188. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/pack/pack.py +0 -0
  189. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/pack/pack_enum.py +0 -0
  190. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/pack/tagCountPack.py +0 -0
  191. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/scripts/__init__.py +0 -0
  192. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/__init__.py +0 -0
  193. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/__init__.py +0 -0
  194. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/amqp/__init__.py +0 -0
  195. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/amqp/kombu.py +0 -0
  196. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/amqp/pika.py +0 -0
  197. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/application/__init__.py +0 -0
  198. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/application/bottle.py +0 -0
  199. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/application/celery.py +0 -0
  200. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/application/cherrypy.py +0 -0
  201. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/application/django.py +0 -0
  202. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/application/django_asgi.py +0 -0
  203. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/application/django_py3.py +0 -0
  204. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/application/fastapi/__init__.py +0 -0
  205. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/application/fastapi/endpoint.py +0 -0
  206. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/application/fastapi/exception_log.py +0 -0
  207. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/application/fastapi/transaction.py +0 -0
  208. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/application/flask.py +0 -0
  209. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/application/frappe.py +0 -0
  210. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/application/graphql.py +0 -0
  211. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/application/nameko.py +0 -0
  212. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/application/odoo.py +0 -0
  213. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/application/starlette.py +0 -0
  214. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/application/tornado.py +0 -0
  215. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/database/__init__.py +0 -0
  216. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/database/cxoracle.py +0 -0
  217. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/database/mongo.py +0 -0
  218. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/database/mysql.py +0 -0
  219. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/database/neo4j.py +0 -0
  220. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/database/psycopg2.py +0 -0
  221. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/database/psycopg3.py +0 -0
  222. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/database/redis.py +0 -0
  223. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/database/sqlalchemy.py +0 -0
  224. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/database/sqlite3.py +0 -0
  225. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/database/util.py +0 -0
  226. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/email/__init__.py +0 -0
  227. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/email/smtp.py +0 -0
  228. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/httpc/__init__.py +0 -0
  229. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/httpc/django.py +0 -0
  230. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/httpc/httplib.py +0 -0
  231. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/httpc/httpx.py +0 -0
  232. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/httpc/requests.py +0 -0
  233. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/httpc/urllib3.py +0 -0
  234. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/mod/logging.py +0 -0
  235. {whatap_python-2.1.4/whatap/trace/mod/standalone → whatap_python-2.2.0/whatap/trace/mod/process}/__init__.py +0 -0
  236. {whatap_python-2.1.4/whatap/util → whatap_python-2.2.0/whatap/trace/mod/standalone}/__init__.py +0 -0
  237. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/simple_trace_context.py +0 -0
  238. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/trace/trace_handler.py +0 -0
  239. {whatap_python-2.1.4/whatap/util/cardinality → whatap_python-2.2.0/whatap/util}/__init__.py +0 -0
  240. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/bit_util.py +0 -0
  241. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/cardinality/hyperloglog.py +0 -0
  242. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/cardinality/murmurhash.py +0 -0
  243. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/cardinality/registerset.py +0 -0
  244. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/compare_util.py +0 -0
  245. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/date_util.py +0 -0
  246. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/debug_util.py +0 -0
  247. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/escape_literal_sql.py +0 -0
  248. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/frame_util.py +0 -0
  249. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/hash_util.py +0 -0
  250. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/hexa32.py +0 -0
  251. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/int_set.py +0 -0
  252. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/ip_util.py +0 -0
  253. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/keygen.py +0 -0
  254. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/linked_list.py +0 -0
  255. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/linked_map.py +0 -0
  256. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/metering_util.py +0 -0
  257. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/request_double_queue.py +0 -0
  258. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/request_queue.py +0 -0
  259. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/string_util.py +0 -0
  260. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/throttle_util.py +0 -0
  261. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/util/userid_util.py +0 -0
  262. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/__init__.py +0 -0
  263. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/blob_value.py +0 -0
  264. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/boolean_value.py +0 -0
  265. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/decimal_value.py +0 -0
  266. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/double_summary.py +0 -0
  267. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/double_value.py +0 -0
  268. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/float_array.py +0 -0
  269. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/float_value.py +0 -0
  270. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/int_array.py +0 -0
  271. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/ip4_value.py +0 -0
  272. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/list_value.py +0 -0
  273. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/long_array.py +0 -0
  274. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/long_summary.py +0 -0
  275. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/map_value.py +0 -0
  276. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/null_value.py +0 -0
  277. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/number_value.py +0 -0
  278. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/summary_value.py +0 -0
  279. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/text_array.py +0 -0
  280. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/text_hash_value.py +0 -0
  281. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/text_value.py +0 -0
  282. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/value.py +0 -0
  283. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/value/value_enum.py +0 -0
  284. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap/whatap.conf +0 -0
  285. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap_python.egg-info/dependency_links.txt +0 -0
  286. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap_python.egg-info/entry_points.txt +0 -0
  287. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap_python.egg-info/not-zip-safe +0 -0
  288. {whatap_python-2.1.4 → whatap_python-2.2.0}/whatap_python.egg-info/requires.txt +0 -0
  289. {whatap_python-2.1.4 → whatap_python-2.2.0}/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.1.4
3
+ Version: 2.2.0
4
4
  Summary: Monitoring and Profiling Service
5
5
  Home-page: https://www.whatap.io
6
6
  Author: whatap
@@ -117,6 +117,37 @@ def whatap_print(*args):
117
117
  message = " ".join(args)
118
118
  print(message)
119
119
 
120
+
121
+ def whatap_error_log(msg, log_id='WHATAP_AGENT_ERROR'):
122
+ """에이전트 시작 오류를 프로세스 로그(stdout)와 hook.log 양쪽에 동일하게 노출한다.
123
+
124
+ hook.log FileHandler 는 Configure.load(display=True)->Logger() 시점에 붙는데,
125
+ go() 스레드가 config() 보다 먼저 도는 경로(자동 attach)나 start_agent 경로에서는
126
+ 아직 핸들러가 없을 수 있다. 그 경우에도 오류가 유실되지 않도록, 핸들러가 없으면
127
+ whatap-hook.log 에 직접 append 한다."""
128
+ # 1) 프로세스 로그 (사용자 애플리케이션 stdout)
129
+ whatap_print(msg)
130
+
131
+ # 2) hook.log
132
+ try:
133
+ if logging.handlers:
134
+ logging.error(msg, extra={'id': log_id})
135
+ return
136
+ except Exception:
137
+ pass
138
+
139
+ # FileHandler 미부착: whatap-hook.log 에 직접 기록
140
+ try:
141
+ logs_dir = os.environ.get('WHATAP_LOGS') or \
142
+ os.path.join(os.environ.get('WHATAP_HOME', '.'), 'logs')
143
+ if not os.path.exists(logs_dir):
144
+ os.makedirs(logs_dir)
145
+ with open(os.path.join(logs_dir, LOG_FILE_NAME), 'a') as f:
146
+ f.write('[{0}] : {1} - {2}\n'.format(
147
+ time.strftime(LOGGING_DATE_FORMAT), log_id, msg))
148
+ except Exception as e:
149
+ whatap_print('WHATAP: hook.log write failed: {}'.format(e))
150
+
120
151
  class Logger(object):
121
152
  def __init__(self):
122
153
  self.logger = logging
@@ -291,6 +322,18 @@ def config(home):
291
322
  from whatap.conf.license import License
292
323
  conf.PCODE = License.getProjectCode(conf.license)
293
324
 
325
+ # LLM 채널은 자기 프로젝트 코드를 가진다. LLM 트랜잭션(is_llm=1)은 LLM Go Agent
326
+ # 소켓으로만 나가므로 결합 좌표에 APM PCODE 를 실으면 서버가 호출자를 엉뚱한
327
+ # 프로젝트에서 찾는다(conf.pcode_of 참조). getProjectCode 는 키 문자열 파싱이라
328
+ # 서버 왕복이 없고, apm_enabled=false 인 LLM 전용 배포에서도 해소된다.
329
+ llm_key = str(getattr(conf, 'llm_accesskey', '') or '').strip()
330
+ if llm_key:
331
+ try:
332
+ from whatap.conf.license import License
333
+ conf.LLM_PCODE = License.getProjectCode(llm_key)
334
+ except Exception as e:
335
+ whatap_print('WHATAP: LLM PCODE RESOLVE ERROR: {}'.format(e))
336
+
294
337
  import whatap.counter
295
338
 
296
339
  hooks(home)
@@ -366,47 +409,61 @@ def hooks(home):
366
409
  from whatap.trace.mod.logging import instrument_logging
367
410
  instrument_logging(logging_module)
368
411
 
412
+ # 한 모듈에 여러 패턴 계열(스텝=instrument_plugin / 워크플로우=instrument_workflow
413
+ # / 트랜잭션=instrument_service)이 공존할 수 있도록, IMPORT_HOOKS[모듈]에
414
+ # 인스트루먼터를 각자의 class_defs 와 함께 리스트로 누적한다. 과거엔 단일 dict 라
415
+ # 뒤 계열이 앞을 덮어써서 같은 모듈에 두 패턴(예: predict=트랜잭션 + 헬퍼=스텝)을
416
+ # 못 걸었다.
417
+ def _register_pattern_hook(module_name, class_def, def_name, mod_path):
418
+ if not PLUGIN.get(module_name):
419
+ PLUGIN[module_name] = []
420
+ if class_def not in PLUGIN[module_name]:
421
+ PLUGIN[module_name].append(class_def) # 호환용(외부 참조)
422
+ existing = IMPORT_HOOKS.get(module_name)
423
+ if isinstance(existing, list):
424
+ entry_list = existing
425
+ elif isinstance(existing, dict):
426
+ entry_list = [existing]
427
+ IMPORT_HOOKS[module_name] = entry_list
428
+ else:
429
+ entry_list = []
430
+ IMPORT_HOOKS[module_name] = entry_list
431
+ for e in entry_list:
432
+ if isinstance(e, dict) and e.get('def') == def_name \
433
+ and e.get('module') == mod_path:
434
+ e.setdefault('class_defs', []).append(class_def)
435
+ return
436
+ entry_list.append({'def': def_name, 'module': mod_path,
437
+ 'class_defs': [class_def]})
438
+
369
439
  if conf.hook_method_patterns:
370
- from whatap.trace.mod.plugin import instrument_plugin
371
- patterns = conf.hook_method_patterns.split(',')
372
- for pattern in patterns:
373
- pattern=pattern.strip()
374
-
375
- module_name, class_def = pattern.split(':')
376
- if not PLUGIN.get(module_name):
377
- PLUGIN[module_name] = []
378
- PLUGIN[module_name].append(class_def)
379
-
380
- DEFINITION["plugin"].append(
381
- (module_name, 'instrument_plugin'))
382
-
383
- key = 'plugin'
384
- for value in DEFINITION[key]:
385
- IMPORT_HOOKS[value[0]] = {'def': value[1],
386
- 'module': '{0}.{1}.{2}.{3}'.format(
387
- 'whatap',
388
- 'trace',
389
- 'mod',
390
- key)}
391
-
392
- # 서비스(트랜잭션) 진입점 자동 래핑 (conf 방법). hook_method_patterns 와 같은
393
- # import-hook 플러밍, 래퍼만 트랜잭션 open/close (whatap.trace.mod.hook_service_patterns).
394
- # @method_profiling 을 소스 수정 없이 conf 로 적용하는 경로.
440
+ for pattern in conf.hook_method_patterns.split(','):
441
+ pattern = pattern.strip()
442
+ if not pattern or ':' not in pattern:
443
+ continue
444
+ module_name, class_def = pattern.split(':', 1)
445
+ _register_pattern_hook(module_name.strip(), class_def.strip(),
446
+ 'instrument_plugin', 'whatap.trace.mod.plugin')
447
+
448
+ # LLM 워크플로우 경계 자동 래핑 (conf 방법). 래퍼만 워크플로우 open/close.
449
+ if conf.workflow_method_patterns:
450
+ for pattern in conf.workflow_method_patterns.split(','):
451
+ pattern = pattern.strip()
452
+ if not pattern or ':' not in pattern:
453
+ continue
454
+ module_name, class_def = pattern.split(':', 1)
455
+ _register_pattern_hook(module_name.strip(), class_def.strip(),
456
+ 'instrument_workflow', 'whatap.trace.mod.workflow_plugin')
457
+
458
+ # 서비스(트랜잭션) 진입점 자동 래핑 (conf 방법). @method_profiling 의 conf 버전.
395
459
  if conf.hook_service_patterns:
396
460
  for pattern in conf.hook_service_patterns.split(','):
397
461
  pattern = pattern.strip()
398
462
  if not pattern or ':' not in pattern:
399
463
  continue
400
464
  module_name, class_def = pattern.split(':', 1)
401
- module_name = module_name.strip()
402
- class_def = class_def.strip()
403
- if not PLUGIN.get(module_name):
404
- PLUGIN[module_name] = []
405
- PLUGIN[module_name].append(class_def)
406
- # 대상 모듈이 PLUGIN 에 등록되면 ImportFinder 가 class_defs 패키징.
407
- IMPORT_HOOKS[module_name] = {
408
- 'def': 'instrument_service',
409
- 'module': 'whatap.trace.mod.hook_service_patterns'}
465
+ _register_pattern_hook(module_name.strip(), class_def.strip(),
466
+ 'instrument_service', 'whatap.trace.mod.hook_service_patterns')
410
467
 
411
468
  if conf.standalone_enabled:
412
469
  if conf.standalone_type == 'multiple-transaction':
@@ -420,9 +477,43 @@ def hooks(home):
420
477
  logging.debug(e, extra={'id': 'PLUGIN ERROR'})
421
478
  finally:
422
479
  sys.meta_path.insert(0, ImportFinder())
480
+ _instrument_already_imported()
423
481
  logging.debug('WHATAP AGENT START!', extra={'id': 'WA000'})
424
482
 
425
483
 
484
+ def _instrument_already_imported():
485
+ """finder 설치 시점에 **이미 import 돼 있던** 훅 대상을 직접 계측한다.
486
+
487
+ ``ImportFinder.find_spec`` 은 import 시스템이 ``sys.modules`` 미스일 때만 호출하므로,
488
+ finder 설치보다 먼저 로드된 모듈은 훅이 영원히 안 걸린다. 그리고 이 에이전트는 자기
489
+ 부팅에 필요한 모듈(``subprocess`` — 최상단 import, 데몬 기동에 사용)을 finder 설치
490
+ **전에** 로드하므로, 그 대상은 DEFINITION 에 등록해도 계측되지 않는다.
491
+
492
+ 앱이 우회할 수 없는 종류의 구멍이다(앱이 할 수 있는 건 에이전트 내부 instrument_*
493
+ 함수를 대신 호출하는 것뿐이고, 그건 자동계측을 쓰는 의미를 없앤다).
494
+
495
+ ``load_module`` 을 그대로 재사용하므로 정상 import 경로와 완전히 같은 처리를 받는다
496
+ (엔트리 리스트/class_defs 패키징 포함). instrument_* 는 멱등이어야 한다 — 이 스윕과 이후의
497
+ 재-import 가 같은 대상을 두 번 건드릴 수 있다.
498
+ """
499
+ try:
500
+ from whatap.trace.trace_import import load_module
501
+ except Exception as e:
502
+ logging.info("pre-imported sweep unavailable: %s: %s",
503
+ e.__class__.__name__, e, exc_info=True)
504
+ return
505
+ for fullname in list(IMPORT_HOOKS.keys()):
506
+ module = sys.modules.get(fullname)
507
+ if module is None:
508
+ continue
509
+ try:
510
+ load_module(module, fullname)
511
+ except Exception as e:
512
+ # 계측 실패는 조용히 넘기면 "데이터가 안 보인다" 로만 드러난다.
513
+ logging.info("failed to inject pre-imported %s: %s: %s",
514
+ fullname, e.__class__.__name__, e, exc_info=True)
515
+
516
+
426
517
  def agent():
427
518
  home = 'WHATAP_HOME'
428
519
  whatap_home = os.environ.get(home)
@@ -451,9 +542,18 @@ def agent():
451
542
  t.start()
452
543
 
453
544
  if llm_enabled == 'true':
454
- t = threading.Thread(target=go, kwargs={'llm': True})
455
- t.setDaemon(True)
456
- t.start()
545
+ # llm_enabled=true 라도 LLM 채널 자격증명(llm_accesskey)이 없으면 LLM
546
+ # 에이전트를 띄우지 않는다. (env 로 플래그만 켜진 경우의 오작동 방지 —
547
+ # llm_accesskey/LLM 서버 호스트 없이 LLM 모드로 도는 것을 막는다.)
548
+ llm_accesskey = (preview_whatap_conf("llm_accesskey")
549
+ or os.environ.get("llm_accesskey"))
550
+ if llm_accesskey:
551
+ t = threading.Thread(target=go, kwargs={'llm': True})
552
+ t.setDaemon(True)
553
+ t.start()
554
+ else:
555
+ whatap_print("WHATAP: llm_enabled=true 이나 llm_accesskey 가 "
556
+ "없어 LLM 에이전트 기동을 건너뜁니다")
457
557
 
458
558
  config(home)
459
559
 
@@ -472,6 +572,17 @@ AGENT_NAME = 'whatap_python'
472
572
 
473
573
 
474
574
  def go(batch=False, opts={}, llm=False):
575
+ # LLM 에이전트는 Linux/macOS 를 지원한다(전용 whatap_python 이
576
+ # $WHATAP_HOME/<os>/<arch> 에 배포됨 — linux/darwin × amd64/arm64).
577
+ # 그 외 OS(Windows 등)는 전용 바이너리가 없으므로 우회적 "binary not found"
578
+ # 대신 명시적으로 LLM 기동을 차단하고 알린다.
579
+ if llm and platform.system().lower() not in ('linux', 'darwin'):
580
+ whatap_error_log(
581
+ 'WHATAP: LLM agent is supported on Linux/macOS only '
582
+ '(current OS: {0}); skipping LLM agent start'.format(platform.system()),
583
+ log_id='WHATAP_LLM_UNSUPPORTED_OS')
584
+ return
585
+
475
586
  newenv=os.environ.copy()
476
587
  newenv['WHATAP_VERSION'] = build.version
477
588
  newenv['whatap.start'] = str(DateUtil.now())
@@ -532,47 +643,78 @@ def go(batch=False, opts={}, llm=False):
532
643
 
533
644
  # Windows uses .exe extension
534
645
  agent_binary_name = AGENT_NAME + '.exe' if sys.platform == 'win32' else AGENT_NAME
535
- dest_agent = os.path.join(home_path, agent_binary_name)
536
646
 
537
- # Try to remove existing binary
538
- # Windows: File may still be locked even after waiting, ignore error and use existing binary
539
- # Unix/Linux: Removal should always succeed
540
- needs_deployment = False
541
- if os.path.exists(dest_agent):
542
- try:
543
- os.remove(dest_agent)
544
- needs_deployment = True
545
- except (OSError, PermissionError) as e:
546
- if sys.platform == 'win32':
547
- # Windows: File is still locked, use existing binary
548
- pass
549
- else:
550
- # Unix/Linux: This should not happen
551
- raise
647
+ # LLM mode (Unix/Linux) runs a dedicated whatap_python binary that ships
648
+ # separately under $WHATAP_HOME/<os>/<arch>/ (e.g. $WHATAP_HOME/linux/amd64).
649
+ # It is executed in place from there rather than deployed from the bundled
650
+ # package into $WHATAP_HOME/whatap_python, which the APM agent uses (the two
651
+ # are now distinct binaries and would otherwise clobber each other).
652
+ run_in_place = llm and sys.platform != 'win32'
653
+
654
+ if run_in_place:
655
+ source_cwd = os.path.join(home_path, platform.system().lower(), ARCH[platform.machine()])
656
+ source_agent = os.path.join(source_cwd, agent_binary_name)
657
+ if not os.path.exists(source_agent):
658
+ raise FileNotFoundError(
659
+ 'LLM agent binary not found: {0} '
660
+ '(place the LLM {1} under $WHATAP_HOME/{2}/{3}/)'.format(
661
+ source_agent, agent_binary_name,
662
+ platform.system().lower(), ARCH[platform.machine()]))
663
+ # Ensure executable (best effort; ignore on read-only locations)
664
+ if not os.access(source_agent, os.X_OK):
665
+ try:
666
+ os.chmod(source_agent, 0o755)
667
+ except OSError as e:
668
+ whatap_print('WHATAP: chmod LLM agent binary failed: %s' % e)
669
+ agent_cmd = source_agent
552
670
  else:
553
- needs_deployment = True
671
+ dest_agent = os.path.join(home_path, agent_binary_name)
554
672
 
555
- # Windows doesn't use architecture subdirectories (only single x64 binary for now)
556
- if sys.platform == 'win32':
557
- source_cwd = os.path.join(os.path.dirname(__file__), 'agent', 'windows')
558
- else:
559
- source_cwd = os.path.join(os.path.join(os.path.dirname(__file__), 'agent'), platform.system().lower(),
560
- ARCH[platform.machine()])
673
+ # Try to remove existing binary
674
+ # Windows: File may still be locked even after waiting, ignore error and use existing binary
675
+ # Unix/Linux: Removal should always succeed
676
+ needs_deployment = False
677
+ if os.path.exists(dest_agent):
678
+ try:
679
+ os.remove(dest_agent)
680
+ needs_deployment = True
681
+ except (OSError, PermissionError) as e:
682
+ if sys.platform == 'win32':
683
+ # Windows: File is still locked, use existing binary
684
+ pass
685
+ else:
686
+ # Unix/Linux: This should not happen
687
+ raise
688
+ else:
689
+ needs_deployment = True
561
690
 
562
- source_agent = os.path.join(source_cwd, agent_binary_name)
691
+ # Windows doesn't use architecture subdirectories (only single x64 binary for now)
692
+ if sys.platform == 'win32':
693
+ source_cwd = os.path.join(os.path.dirname(__file__), 'agent', 'windows')
694
+ else:
695
+ source_cwd = os.path.join(os.path.join(os.path.dirname(__file__), 'agent'), platform.system().lower(),
696
+ ARCH[platform.machine()])
563
697
 
564
- # Deploy agent binary only if needed
565
- if needs_deployment:
566
- # Try symlink first (Unix/Linux or Windows with proper permissions)
567
- try:
568
- os.symlink(source_agent, dest_agent)
569
- except (OSError, NotImplementedError):
570
- # Fallback to copy on Windows or when symlink fails
571
- import shutil
572
- shutil.copy2(source_agent, dest_agent)
573
- # Make executable on Unix-like systems
574
- if sys.platform != 'win32':
575
- os.chmod(dest_agent, 0o755)
698
+ source_agent = os.path.join(source_cwd, agent_binary_name)
699
+
700
+ # Deploy agent binary only if needed
701
+ if needs_deployment:
702
+ # Try symlink first (Unix/Linux or Windows with proper permissions)
703
+ try:
704
+ os.symlink(source_agent, dest_agent)
705
+ except (OSError, NotImplementedError):
706
+ # Fallback to copy on Windows or when symlink fails
707
+ import shutil
708
+ shutil.copy2(source_agent, dest_agent)
709
+ # Make executable on Unix-like systems
710
+ if sys.platform != 'win32':
711
+ os.chmod(dest_agent, 0o755)
712
+
713
+ # Windows uses absolute path, Unix/Linux uses relative path
714
+ if sys.platform == 'win32':
715
+ agent_cmd = os.path.join(home_path, agent_binary_name)
716
+ else:
717
+ agent_cmd = './{0}'.format(agent_binary_name)
576
718
 
577
719
  sockfile_path = os.path.join(home_path, 'run')
578
720
  if not os.path.exists(sockfile_path):
@@ -581,12 +723,6 @@ def go(batch=False, opts={}, llm=False):
581
723
  newenv['WHATAP_PID_FILE'] = file_name
582
724
  newenv['PYTHON_PARENT_APP_PID'] = str(os.getpid())
583
725
 
584
- # Windows uses absolute path, Unix/Linux uses relative path
585
- if sys.platform == 'win32':
586
- agent_cmd = os.path.join(home_path, agent_binary_name)
587
- else:
588
- agent_cmd = './{0}'.format(agent_binary_name)
589
-
590
726
  # Windows: Run in background with DETACHED_PROCESS
591
727
  # Unix/Linux: Run in background with start_new_session
592
728
  # 실행 인자 구성
@@ -657,7 +793,10 @@ def go(batch=False, opts={}, llm=False):
657
793
  except Exception as e:
658
794
  import traceback
659
795
  traceback.print_exc()
660
- whatap_print('WHATAP: AGENT ERROR: {}'.format(e))
796
+ # hook.log + 프로세스 로그 양쪽에 동일하게 노출
797
+ whatap_error_log(
798
+ 'WHATAP: {0} AGENT ERROR: {1}'.format('LLM' if llm else 'APM', e),
799
+ log_id='WHATAP_LLM_AGENT_ERROR' if llm else 'WHATAP_AGENT_ERROR')
661
800
  else:
662
801
  whatap_print('WHATAP: AGENT UP! (process name: {}{})\n'.format(AGENT_NAME, ' --llm' if llm else ''))
663
802
 
@@ -695,8 +834,8 @@ def _service_trace(fn, service_name=None):
695
834
  """fn 을 하나의 트랜잭션(TX_START~TX_END)으로 감싼 래퍼를 반환한다.
696
835
 
697
836
  ``@method_profiling`` 데코레이터와 conf ``hook_service_patterns``
698
- (whatap.trace.mod.hook_service_patterns) 의 **공용 코어**. 여기엔 agent() 부팅이
699
- 없다 (데코레이터/부트 경로가 각자 처리).
837
+ (whatap.trace.mod.hook_service_patterns) 의 **공용 코어**. 여기엔 agent() 부팅이 없다
838
+ (데코레이터/부트 경로가 각자 처리).
700
839
 
701
840
  코루틴 함수는 동기 래퍼로 감싸면 트랜잭션이 start_interceptor → 코루틴 생성 →
702
841
  end_interceptor 로 즉시 열렸다 닫히고, 실제 본문은 호출자가 await 할 때 트랜잭션
@@ -772,26 +911,7 @@ def _service_trace(fn, service_name=None):
772
911
 
773
912
 
774
913
  def method_profiling(fn):
775
- # NOTE: 의도적으로 _service_trace 를 쓰지 않는다. 이 데코레이터는 이미 배포된
776
- # 공개 API 라, 예외 재전파/컨텍스트 정리/async 지원으로 바꾸면 기존 사용자의
777
- # 동작이 달라진다. 해당 수정은 dev 에 있고 정식 머지 때 함께 들어온다.
778
- def trace(*args, **kwargs):
779
- callback = None
780
- try:
781
- ctx = TraceContext()
782
- ctx.service_name=fn.__name__
783
- start_interceptor(ctx)
784
- callback = fn(*args, **kwargs)
785
- except Exception as e:
786
- ctx = TraceContextManager.getLocalContext()
787
- interceptor_step_error(e, ctx=ctx)
788
- interceptor_error_log(ctx.id, e, fn, args, kwargs)
789
- logging.debug('WHATAP(@method_profiling): ' + str(e),
790
- extra={'id': 'WA776'}, exc_info=True)
791
- finally:
792
- ctx = TraceContextManager.getLocalContext()
793
- end_interceptor(ctx=ctx)
794
- return callback
914
+ trace = _service_trace(fn)
795
915
 
796
916
  if not os.environ.get('whatap.enabled'):
797
917
  agent()
@@ -0,0 +1,4 @@
1
+ app = 'Python'
2
+ name = 'whatap-python'
3
+ version = '2.2.0'
4
+ release_date = '20260901'