whatap-python 2.1.4rc1__tar.gz → 2.2.1rc1__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 (290) hide show
  1. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/PKG-INFO +1 -1
  2. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/__init__.py +311 -106
  3. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/agent/linux/amd64/whatap_python +0 -0
  4. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/agent/linux/arm64/whatap_python +0 -0
  5. whatap_python-2.2.1rc1/whatap/build.py +4 -0
  6. whatap_python-2.2.1rc1/whatap/conf/configuration.py +444 -0
  7. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/conf/configure.py +55 -0
  8. whatap_python-2.2.1rc1/whatap/counter/__init__.py +36 -0
  9. whatap_python-2.2.1rc1/whatap/counter/tasks/base_task.py +35 -0
  10. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/counter/tasks/llm_evaluator_task.py +56 -10
  11. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/counter/tasks/llm_log_sink_task.py +85 -19
  12. whatap_python-2.2.1rc1/whatap/counter/tasks/llm_stat_task.py +138 -0
  13. whatap_python-2.2.1rc1/whatap/llm/__init__.py +70 -0
  14. whatap_python-2.2.1rc1/whatap/llm/api/__init__.py +20 -0
  15. whatap_python-2.2.1rc1/whatap/llm/api/agent_name.py +83 -0
  16. {whatap_python-2.1.4rc1/whatap/llm → whatap_python-2.2.1rc1/whatap/llm/api}/prompt_meta.py +31 -4
  17. whatap_python-2.2.1rc1/whatap/llm/api/session.py +175 -0
  18. whatap_python-2.2.1rc1/whatap/llm/api/turn.py +88 -0
  19. whatap_python-2.2.1rc1/whatap/llm/api/user.py +176 -0
  20. whatap_python-2.2.1rc1/whatap/llm/api/workflow.py +115 -0
  21. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/__init__.py +4 -0
  22. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/base.py +15 -11
  23. whatap_python-2.2.1rc1/whatap/llm/evaluators/builtins/_json_repair.py +106 -0
  24. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/builtins/combined_judge.py +7 -4
  25. whatap_python-2.2.1rc1/whatap/llm/evaluators/builtins/judge_factories.py +90 -0
  26. whatap_python-2.2.1rc1/whatap/llm/evaluators/builtins/llm_judge.py +248 -0
  27. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/builtins/pii_leak.py +5 -20
  28. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/builtins/url_scan.py +21 -22
  29. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/registry.py +91 -0
  30. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/scope.py +22 -2
  31. whatap_python-2.2.1rc1/whatap/llm/instrument/__init__.py +32 -0
  32. whatap_python-2.2.1rc1/whatap/llm/instrument/_boundary.py +492 -0
  33. whatap_python-2.2.1rc1/whatap/llm/instrument/agent.py +35 -0
  34. whatap_python-2.2.1rc1/whatap/llm/instrument/llm_api.py +89 -0
  35. whatap_python-2.2.1rc1/whatap/llm/instrument/workflow.py +42 -0
  36. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/log_sink_packs/llm_log_sink_pack.py +32 -0
  37. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/log_sink_packs/llm_tx_status.py +51 -0
  38. whatap_python-2.2.1rc1/whatap/llm/orchestration/__init__.py +16 -0
  39. whatap_python-2.2.1rc1/whatap/llm/orchestration/hermes/__init__.py +143 -0
  40. whatap_python-2.2.1rc1/whatap/llm/orchestration/hermes/gateway.py +172 -0
  41. whatap_python-2.2.1rc1/whatap/llm/orchestration/hermes/kanban.py +353 -0
  42. whatap_python-2.2.1rc1/whatap/llm/orchestration/langgraph/__init__.py +134 -0
  43. whatap_python-2.2.1rc1/whatap/llm/pricing.py +187 -0
  44. whatap_python-2.2.1rc1/whatap/llm/providers/anthropic/__init__.py +72 -0
  45. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/anthropic/messages/messages_context.py +48 -0
  46. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/anthropic/messages/messages_extractor.py +53 -0
  47. whatap_python-2.2.1rc1/whatap/llm/providers/anthropic/messages/messages_stream.py +162 -0
  48. whatap_python-2.2.1rc1/whatap/llm/providers/interceptor.py +349 -0
  49. whatap_python-2.2.1rc1/whatap/llm/providers/openai/__init__.py +110 -0
  50. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/chat/chat_context.py +6 -0
  51. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/content_parser.py +4 -1
  52. whatap_python-2.2.1rc1/whatap/llm/providers/stream_accumulator.py +287 -0
  53. whatap_python-2.2.1rc1/whatap/llm/providers/workflow.py +681 -0
  54. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/__init__.py +6 -0
  55. whatap_python-2.2.1rc1/whatap/llm/stats/active_agent_stat.py +93 -0
  56. whatap_python-2.2.1rc1/whatap/llm/stats/active_stat.py +83 -0
  57. whatap_python-2.2.1rc1/whatap/llm/stats/actx_stat.py +80 -0
  58. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/base_stat.py +6 -0
  59. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/eval_stat.py +1 -0
  60. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/pii_leak_eval_stat.py +2 -1
  61. whatap_python-2.2.1rc1/whatap/llm/stats/traj_actx_stat.py +84 -0
  62. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/url_scan_eval_stat.py +2 -1
  63. whatap_python-2.2.1rc1/whatap/net/async_sender.py +313 -0
  64. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/net/packet_enum.py +1 -1
  65. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/net/param_def.py +11 -0
  66. whatap_python-2.2.1rc1/whatap/net/udp_session.py +790 -0
  67. whatap_python-2.2.1rc1/whatap/net/udp_thread.py +103 -0
  68. whatap_python-2.2.1rc1/whatap/trace/mod/application/fastapi/instrumentation.py +403 -0
  69. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/fastapi/scope.py +17 -0
  70. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/wsgi.py +19 -1
  71. whatap_python-2.2.1rc1/whatap/trace/mod/hook_service_patterns.py +26 -0
  72. whatap_python-2.2.1rc1/whatap/trace/mod/httpc/util.py +689 -0
  73. whatap_python-2.2.1rc1/whatap/trace/mod/pattern_targets.py +137 -0
  74. whatap_python-2.2.1rc1/whatap/trace/mod/plugin.py +47 -0
  75. whatap_python-2.2.1rc1/whatap/trace/mod/process/subprocess_spawn.py +390 -0
  76. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/standalone/multiple.py +2 -1
  77. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/standalone/single.py +2 -1
  78. whatap_python-2.2.1rc1/whatap/trace/mod/thread_context.py +202 -0
  79. whatap_python-2.2.1rc1/whatap/trace/mod/workflow_plugin.py +33 -0
  80. whatap_python-2.2.1rc1/whatap/trace/mtrace_env.py +368 -0
  81. whatap_python-2.2.1rc1/whatap/trace/patch.py +98 -0
  82. whatap_python-2.2.1rc1/whatap/trace/trace_context.py +330 -0
  83. whatap_python-2.2.1rc1/whatap/trace/trace_context_manager.py +1005 -0
  84. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/trace_error.py +8 -2
  85. whatap_python-2.2.1rc1/whatap/trace/trace_import.py +136 -0
  86. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/trace_module_definition.py +28 -0
  87. whatap_python-2.2.1rc1/whatap/util/cardinality/__init__.py +0 -0
  88. whatap_python-2.2.1rc1/whatap/util/native_process.py +458 -0
  89. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap_python.egg-info/PKG-INFO +1 -1
  90. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap_python.egg-info/SOURCES.txt +32 -3
  91. whatap_python-2.1.4rc1/tests/test_stream_ttft.py +0 -192
  92. whatap_python-2.1.4rc1/whatap/build.py +0 -4
  93. whatap_python-2.1.4rc1/whatap/conf/configuration.py +0 -284
  94. whatap_python-2.1.4rc1/whatap/counter/__init__.py +0 -14
  95. whatap_python-2.1.4rc1/whatap/counter/tasks/base_task.py +0 -26
  96. whatap_python-2.1.4rc1/whatap/counter/tasks/llm_stat_task.py +0 -78
  97. whatap_python-2.1.4rc1/whatap/llm/__init__.py +0 -17
  98. whatap_python-2.1.4rc1/whatap/llm/evaluators/builtins/llm_judge.py +0 -516
  99. whatap_python-2.1.4rc1/whatap/llm/pricing.py +0 -236
  100. whatap_python-2.1.4rc1/whatap/llm/providers/anthropic/__init__.py +0 -37
  101. whatap_python-2.1.4rc1/whatap/llm/providers/interceptor.py +0 -232
  102. whatap_python-2.1.4rc1/whatap/llm/providers/openai/__init__.py +0 -133
  103. whatap_python-2.1.4rc1/whatap/llm/providers/stream_accumulator.py +0 -139
  104. whatap_python-2.1.4rc1/whatap/llm/stats/active_stat.py +0 -86
  105. whatap_python-2.1.4rc1/whatap/llm/stats/meter.py +0 -18
  106. whatap_python-2.1.4rc1/whatap/net/async_sender.py +0 -107
  107. whatap_python-2.1.4rc1/whatap/net/udp_session.py +0 -396
  108. whatap_python-2.1.4rc1/whatap/net/udp_thread.py +0 -54
  109. whatap_python-2.1.4rc1/whatap/trace/mod/application/fastapi/instrumentation.py +0 -204
  110. whatap_python-2.1.4rc1/whatap/trace/mod/hook_service_patterns.py +0 -72
  111. whatap_python-2.1.4rc1/whatap/trace/mod/httpc/util.py +0 -388
  112. whatap_python-2.1.4rc1/whatap/trace/mod/plugin.py +0 -84
  113. whatap_python-2.1.4rc1/whatap/trace/trace_context.py +0 -212
  114. whatap_python-2.1.4rc1/whatap/trace/trace_context_manager.py +0 -255
  115. whatap_python-2.1.4rc1/whatap/trace/trace_import.py +0 -99
  116. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/README.md +0 -0
  117. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/pyproject.toml +0 -0
  118. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/setup.cfg +0 -0
  119. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/setup.py +0 -0
  120. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/LICENSE +0 -0
  121. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/README.rst +0 -0
  122. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/__main__.py +0 -0
  123. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/agent/darwin/amd64/whatap_python +0 -0
  124. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/agent/darwin/arm64/whatap_python +0 -0
  125. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/agent/windows/whatap_python.exe +0 -0
  126. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/bootstrap/__init__.py +0 -0
  127. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/bootstrap/sitecustomize.py +0 -0
  128. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/conf/__init__.py +0 -0
  129. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/conf/license.py +0 -0
  130. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/control/__init__.py +0 -0
  131. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/counter/counter_manager.py +0 -0
  132. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/counter/tasks/__init__.py +0 -0
  133. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/counter/tasks/openfiledescriptor.py +0 -0
  134. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/io/__init__.py +0 -0
  135. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/io/data_inputx.py +0 -0
  136. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/io/data_outputx.py +0 -0
  137. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/definitions.py +0 -0
  138. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/builtins/__init__.py +0 -0
  139. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/builtins/answer_relevance.py +0 -0
  140. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/builtins/factuality.py +0 -0
  141. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/builtins/hallucination.py +0 -0
  142. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/builtins/prompt_injection.py +0 -0
  143. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/builtins/toxicity.py +0 -0
  144. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/evaluators/sampler.py +0 -0
  145. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/features.py +0 -0
  146. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/log_sink_packs/__init__.py +0 -0
  147. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/log_sink_packs/llm_input_message.py +0 -0
  148. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/log_sink_packs/llm_output_message.py +0 -0
  149. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/log_sink_packs/llm_step_eval_status.py +0 -0
  150. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/log_sink_packs/llm_step_status.py +0 -0
  151. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/log_sink_packs/llm_system_message.py +0 -0
  152. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/log_sink_packs/llm_tool_calls.py +0 -0
  153. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/log_sink_packs/llm_tool_results.py +0 -0
  154. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/__init__.py +0 -0
  155. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/anthropic/messages/__init__.py +0 -0
  156. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/anthropic/messages/messages.py +0 -0
  157. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/chat/__init__.py +0 -0
  158. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/chat/chat.py +0 -0
  159. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/chat/chat_extractor.py +0 -0
  160. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/completions/__init__.py +0 -0
  161. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/completions/completions.py +0 -0
  162. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/completions/completions_context.py +0 -0
  163. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/completions/completions_extractor.py +0 -0
  164. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/embeddings/__init__.py +0 -0
  165. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/embeddings/embeddings.py +0 -0
  166. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/embeddings/embeddings_context.py +0 -0
  167. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/embeddings/embeddings_extractor.py +0 -0
  168. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/responses/__init__.py +0 -0
  169. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/responses/responses.py +0 -0
  170. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/responses/responses_context.py +0 -0
  171. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/providers/openai/responses/responses_extractor.py +0 -0
  172. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/answer_relevance_eval_stat.py +0 -0
  173. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/api_status_stat.py +0 -0
  174. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/combined_judge_eval_stat.py +0 -0
  175. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/error_stat.py +0 -0
  176. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/factuality_eval_stat.py +0 -0
  177. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/feature_stat.py +0 -0
  178. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/finish_stat.py +0 -0
  179. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/hallucination_eval_stat.py +0 -0
  180. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/perf_stat.py +0 -0
  181. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/prompt_injection_eval_stat.py +0 -0
  182. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/token_usage_stat.py +0 -0
  183. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/llm/stats/toxicity_eval_stat.py +0 -0
  184. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/net/__init__.py +0 -0
  185. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/net/packet_type_enum.py +0 -0
  186. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/net/stackhelper.py +0 -0
  187. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/pack/__init__.py +0 -0
  188. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/pack/logSinkPack.py +0 -0
  189. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/pack/pack.py +0 -0
  190. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/pack/pack_enum.py +0 -0
  191. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/pack/tagCountPack.py +0 -0
  192. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/scripts/__init__.py +0 -0
  193. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/__init__.py +0 -0
  194. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/__init__.py +0 -0
  195. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/amqp/__init__.py +0 -0
  196. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/amqp/kombu.py +0 -0
  197. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/amqp/pika.py +0 -0
  198. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/__init__.py +0 -0
  199. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/bottle.py +0 -0
  200. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/celery.py +0 -0
  201. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/cherrypy.py +0 -0
  202. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/django.py +0 -0
  203. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/django_asgi.py +0 -0
  204. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/django_py3.py +0 -0
  205. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/fastapi/__init__.py +0 -0
  206. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/fastapi/endpoint.py +0 -0
  207. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/fastapi/exception_log.py +0 -0
  208. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/fastapi/transaction.py +0 -0
  209. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/flask.py +0 -0
  210. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/frappe.py +0 -0
  211. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/graphql.py +0 -0
  212. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/nameko.py +0 -0
  213. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/odoo.py +0 -0
  214. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/starlette.py +0 -0
  215. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/application/tornado.py +0 -0
  216. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/__init__.py +0 -0
  217. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/cxoracle.py +0 -0
  218. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/mongo.py +0 -0
  219. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/mysql.py +0 -0
  220. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/neo4j.py +0 -0
  221. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/psycopg2.py +0 -0
  222. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/psycopg3.py +0 -0
  223. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/redis.py +0 -0
  224. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/sqlalchemy.py +0 -0
  225. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/sqlite3.py +0 -0
  226. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/database/util.py +0 -0
  227. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/email/__init__.py +0 -0
  228. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/email/smtp.py +0 -0
  229. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/httpc/__init__.py +0 -0
  230. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/httpc/django.py +0 -0
  231. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/httpc/httplib.py +0 -0
  232. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/httpc/httpx.py +0 -0
  233. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/httpc/requests.py +0 -0
  234. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/httpc/urllib3.py +0 -0
  235. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/mod/logging.py +0 -0
  236. {whatap_python-2.1.4rc1/whatap/trace/mod/standalone → whatap_python-2.2.1rc1/whatap/trace/mod/process}/__init__.py +0 -0
  237. {whatap_python-2.1.4rc1/whatap/util → whatap_python-2.2.1rc1/whatap/trace/mod/standalone}/__init__.py +0 -0
  238. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/simple_trace_context.py +0 -0
  239. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/trace/trace_handler.py +0 -0
  240. {whatap_python-2.1.4rc1/whatap/util/cardinality → whatap_python-2.2.1rc1/whatap/util}/__init__.py +0 -0
  241. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/bit_util.py +0 -0
  242. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/cardinality/hyperloglog.py +0 -0
  243. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/cardinality/murmurhash.py +0 -0
  244. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/cardinality/registerset.py +0 -0
  245. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/compare_util.py +0 -0
  246. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/date_util.py +0 -0
  247. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/debug_util.py +0 -0
  248. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/escape_literal_sql.py +0 -0
  249. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/frame_util.py +0 -0
  250. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/hash_util.py +0 -0
  251. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/hexa32.py +0 -0
  252. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/int_set.py +0 -0
  253. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/ip_util.py +0 -0
  254. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/keygen.py +0 -0
  255. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/linked_list.py +0 -0
  256. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/linked_map.py +0 -0
  257. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/metering_util.py +0 -0
  258. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/request_double_queue.py +0 -0
  259. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/request_queue.py +0 -0
  260. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/string_util.py +0 -0
  261. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/throttle_util.py +0 -0
  262. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/util/userid_util.py +0 -0
  263. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/__init__.py +0 -0
  264. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/blob_value.py +0 -0
  265. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/boolean_value.py +0 -0
  266. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/decimal_value.py +0 -0
  267. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/double_summary.py +0 -0
  268. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/double_value.py +0 -0
  269. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/float_array.py +0 -0
  270. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/float_value.py +0 -0
  271. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/int_array.py +0 -0
  272. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/ip4_value.py +0 -0
  273. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/list_value.py +0 -0
  274. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/long_array.py +0 -0
  275. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/long_summary.py +0 -0
  276. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/map_value.py +0 -0
  277. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/null_value.py +0 -0
  278. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/number_value.py +0 -0
  279. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/summary_value.py +0 -0
  280. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/text_array.py +0 -0
  281. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/text_hash_value.py +0 -0
  282. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/text_value.py +0 -0
  283. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/value.py +0 -0
  284. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/value/value_enum.py +0 -0
  285. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap/whatap.conf +0 -0
  286. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap_python.egg-info/dependency_links.txt +0 -0
  287. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap_python.egg-info/entry_points.txt +0 -0
  288. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap_python.egg-info/not-zip-safe +0 -0
  289. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/whatap_python.egg-info/requires.txt +0 -0
  290. {whatap_python-2.1.4rc1 → whatap_python-2.2.1rc1}/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.4rc1
3
+ Version: 2.2.1rc1
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)
@@ -446,14 +537,28 @@ def agent():
446
537
  llm_enabled = preview_whatap_conf("llm_enabled")
447
538
 
448
539
  if apm_enabled == 'true':
449
- t = threading.Thread(target=go)
450
- t.setDaemon(True)
451
- t.start()
540
+ if sys.platform.startswith('linux'):
541
+ # Publish native ownership before a short-lived interpreter
542
+ # can exit. This does not wait for collector/TCP readiness.
543
+ go()
544
+ else:
545
+ t = threading.Thread(target=go)
546
+ t.setDaemon(True)
547
+ t.start()
452
548
 
453
549
  if llm_enabled == 'true':
454
- t = threading.Thread(target=go, kwargs={'llm': True})
455
- t.setDaemon(True)
456
- t.start()
550
+ # llm_enabled=true 라도 LLM 채널 자격증명(llm_accesskey)이 없으면 LLM
551
+ # 에이전트를 띄우지 않는다. (env 로 플래그만 켜진 경우의 오작동 방지 —
552
+ # llm_accesskey/LLM 서버 호스트 없이 LLM 모드로 도는 것을 막는다.)
553
+ llm_accesskey = (preview_whatap_conf("llm_accesskey")
554
+ or os.environ.get("llm_accesskey"))
555
+ if llm_accesskey:
556
+ t = threading.Thread(target=go, kwargs={'llm': True})
557
+ t.setDaemon(True)
558
+ t.start()
559
+ else:
560
+ whatap_print("WHATAP: llm_enabled=true 이나 llm_accesskey 가 "
561
+ "없어 LLM 에이전트 기동을 건너뜁니다")
457
562
 
458
563
  config(home)
459
564
 
@@ -471,7 +576,71 @@ AGENT_NAME = 'whatap_python'
471
576
 
472
577
 
473
578
 
579
+ def _native_agent_spec(opts):
580
+ from whatap.util.native_process import AgentSpec
581
+ source = os.path.join(os.path.dirname(__file__), 'agent', 'linux',
582
+ ARCH[platform.machine()], AGENT_NAME)
583
+ env = os.environ.copy()
584
+ env.update(opts)
585
+ return AgentSpec(os.environ['WHATAP_HOME'], source, env)
586
+
587
+
588
+ def _native_agent_log_path():
589
+ return os.path.join(os.environ.get('WHATAP_HOME', '.'), 'logs', AGENT_NAME + '.log')
590
+
591
+
592
+ def _open_native_agent_log():
593
+ # managed 경로는 PIPE 를 읽지 않으므로 네이티브 stdout/stderr 를 파일로 남겨
594
+ # 기동 직후 실패 원인을 추적할 수 있게 한다.
595
+ try:
596
+ log_path = _native_agent_log_path()
597
+ os.makedirs(os.path.dirname(log_path), exist_ok=True)
598
+ return open(log_path, 'ab')
599
+ except Exception as e:
600
+ whatap_print('WHATAP: native agent log open failed: %s' % e)
601
+ return subprocess.DEVNULL
602
+
603
+
474
604
  def go(batch=False, opts={}, llm=False):
605
+ if sys.platform.startswith('linux') and not batch and not llm:
606
+ from whatap.util.native_process import FAILED, ensure_agent_running
607
+
608
+ def on_child_exit(pid, exit_code):
609
+ whatap_error_log(
610
+ 'WHATAP: native APM exited (PID: {}, exit code: {}, log: {})'.format(
611
+ pid, exit_code, _native_agent_log_path()),
612
+ log_id='WHATAP_AGENT_EXITED')
613
+
614
+ try:
615
+ spec = _native_agent_spec(opts)
616
+ result = ensure_agent_running(
617
+ spec, lambda: _start_go(batch, opts, llm, managed=True),
618
+ on_child_exit=on_child_exit)
619
+ if result.status == FAILED:
620
+ whatap_error_log('WHATAP: native APM startup failed',
621
+ log_id='WHATAP_AGENT_START_FAILED')
622
+ else:
623
+ whatap_print('WHATAP: native APM {} (PID: {})'.format(
624
+ result.status, result.pid))
625
+ except Exception as e:
626
+ whatap_error_log('WHATAP: native APM startup failed: {}'.format(e),
627
+ log_id='WHATAP_AGENT_START_FAILED')
628
+ return
629
+ return _start_go(batch, opts, llm)
630
+
631
+
632
+ def _start_go(batch=False, opts={}, llm=False, managed=False):
633
+ # LLM 에이전트는 Linux/macOS 를 지원한다(전용 whatap_python 이
634
+ # $WHATAP_HOME/<os>/<arch> 에 배포됨 — linux/darwin × amd64/arm64).
635
+ # 그 외 OS(Windows 등)는 전용 바이너리가 없으므로 우회적 "binary not found"
636
+ # 대신 명시적으로 LLM 기동을 차단하고 알린다.
637
+ if llm and platform.system().lower() not in ('linux', 'darwin'):
638
+ whatap_error_log(
639
+ 'WHATAP: LLM agent is supported on Linux/macOS only '
640
+ '(current OS: {0}); skipping LLM agent start'.format(platform.system()),
641
+ log_id='WHATAP_LLM_UNSUPPORTED_OS')
642
+ return
643
+
475
644
  newenv=os.environ.copy()
476
645
  newenv['WHATAP_VERSION'] = build.version
477
646
  newenv['whatap.start'] = str(DateUtil.now())
@@ -514,7 +683,7 @@ def go(batch=False, opts={}, llm=False):
514
683
  return ''
515
684
 
516
685
  pid = read_file(home, file_name)
517
- if pid and get_pname(pid).find('whatap_python') >= 0:
686
+ if not managed and pid and get_pname(pid).find('whatap_python') >= 0:
518
687
  if sys.platform == 'win32':
519
688
  # Windows: Don't kill, just skip
520
689
  whatap_print('WHATAP: Agent already running (PID: {}). Skipping duplicate execution.'.format(pid))
@@ -532,47 +701,78 @@ def go(batch=False, opts={}, llm=False):
532
701
 
533
702
  # Windows uses .exe extension
534
703
  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
704
 
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
705
+ # LLM mode (Unix/Linux) runs a dedicated whatap_python binary that ships
706
+ # separately under $WHATAP_HOME/<os>/<arch>/ (e.g. $WHATAP_HOME/linux/amd64).
707
+ # It is executed in place from there rather than deployed from the bundled
708
+ # package into $WHATAP_HOME/whatap_python, which the APM agent uses (the two
709
+ # are now distinct binaries and would otherwise clobber each other).
710
+ run_in_place = llm and sys.platform != 'win32'
711
+
712
+ if run_in_place:
713
+ source_cwd = os.path.join(home_path, platform.system().lower(), ARCH[platform.machine()])
714
+ source_agent = os.path.join(source_cwd, agent_binary_name)
715
+ if not os.path.exists(source_agent):
716
+ raise FileNotFoundError(
717
+ 'LLM agent binary not found: {0} '
718
+ '(place the LLM {1} under $WHATAP_HOME/{2}/{3}/)'.format(
719
+ source_agent, agent_binary_name,
720
+ platform.system().lower(), ARCH[platform.machine()]))
721
+ # Ensure executable (best effort; ignore on read-only locations)
722
+ if not os.access(source_agent, os.X_OK):
723
+ try:
724
+ os.chmod(source_agent, 0o755)
725
+ except OSError as e:
726
+ whatap_print('WHATAP: chmod LLM agent binary failed: %s' % e)
727
+ agent_cmd = source_agent
552
728
  else:
553
- needs_deployment = True
729
+ dest_agent = os.path.join(home_path, agent_binary_name)
554
730
 
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()])
731
+ # Try to remove existing binary
732
+ # Windows: File may still be locked even after waiting, ignore error and use existing binary
733
+ # Unix/Linux: Removal should always succeed
734
+ needs_deployment = False
735
+ if os.path.exists(dest_agent):
736
+ try:
737
+ os.remove(dest_agent)
738
+ needs_deployment = True
739
+ except (OSError, PermissionError) as e:
740
+ if sys.platform == 'win32':
741
+ # Windows: File is still locked, use existing binary
742
+ pass
743
+ else:
744
+ # Unix/Linux: This should not happen
745
+ raise
746
+ else:
747
+ needs_deployment = True
561
748
 
562
- source_agent = os.path.join(source_cwd, agent_binary_name)
749
+ # Windows doesn't use architecture subdirectories (only single x64 binary for now)
750
+ if sys.platform == 'win32':
751
+ source_cwd = os.path.join(os.path.dirname(__file__), 'agent', 'windows')
752
+ else:
753
+ source_cwd = os.path.join(os.path.join(os.path.dirname(__file__), 'agent'), platform.system().lower(),
754
+ ARCH[platform.machine()])
563
755
 
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)
756
+ source_agent = os.path.join(source_cwd, agent_binary_name)
757
+
758
+ # Deploy agent binary only if needed
759
+ if needs_deployment:
760
+ # Try symlink first (Unix/Linux or Windows with proper permissions)
761
+ try:
762
+ os.symlink(source_agent, dest_agent)
763
+ except (OSError, NotImplementedError):
764
+ # Fallback to copy on Windows or when symlink fails
765
+ import shutil
766
+ shutil.copy2(source_agent, dest_agent)
767
+ # Make executable on Unix-like systems
768
+ if sys.platform != 'win32':
769
+ os.chmod(dest_agent, 0o755)
770
+
771
+ # Windows uses absolute path, Unix/Linux uses relative path
772
+ if sys.platform == 'win32':
773
+ agent_cmd = os.path.join(home_path, agent_binary_name)
774
+ else:
775
+ agent_cmd = './{0}'.format(agent_binary_name)
576
776
 
577
777
  sockfile_path = os.path.join(home_path, 'run')
578
778
  if not os.path.exists(sockfile_path):
@@ -581,12 +781,6 @@ def go(batch=False, opts={}, llm=False):
581
781
  newenv['WHATAP_PID_FILE'] = file_name
582
782
  newenv['PYTHON_PARENT_APP_PID'] = str(os.getpid())
583
783
 
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
784
  # Windows: Run in background with DETACHED_PROCESS
591
785
  # Unix/Linux: Run in background with start_new_session
592
786
  # 실행 인자 구성
@@ -638,6 +832,17 @@ def go(batch=False, opts={}, llm=False):
638
832
  cwd=home_path, env=newenv,
639
833
  creationflags=DETACHED_PROCESS,
640
834
  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
835
+ elif managed:
836
+ go_out = _open_native_agent_log()
837
+ try:
838
+ process = subprocess.Popen(cmd_args,
839
+ cwd=home_path, env=newenv,
840
+ start_new_session=True,
841
+ stdout=go_out, stderr=go_out)
842
+ finally:
843
+ if go_out is not subprocess.DEVNULL:
844
+ go_out.close()
845
+ return process
641
846
  else:
642
847
  process = subprocess.Popen(cmd_args,
643
848
  cwd=home_path, env=newenv,
@@ -657,7 +862,10 @@ def go(batch=False, opts={}, llm=False):
657
862
  except Exception as e:
658
863
  import traceback
659
864
  traceback.print_exc()
660
- whatap_print('WHATAP: AGENT ERROR: {}'.format(e))
865
+ # hook.log + 프로세스 로그 양쪽에 동일하게 노출
866
+ whatap_error_log(
867
+ 'WHATAP: {0} AGENT ERROR: {1}'.format('LLM' if llm else 'APM', e),
868
+ log_id='WHATAP_LLM_AGENT_ERROR' if llm else 'WHATAP_AGENT_ERROR')
661
869
  else:
662
870
  whatap_print('WHATAP: AGENT UP! (process name: {}{})\n'.format(AGENT_NAME, ' --llm' if llm else ''))
663
871
 
@@ -695,8 +903,8 @@ def _service_trace(fn, service_name=None):
695
903
  """fn 을 하나의 트랜잭션(TX_START~TX_END)으로 감싼 래퍼를 반환한다.
696
904
 
697
905
  ``@method_profiling`` 데코레이터와 conf ``hook_service_patterns``
698
- (whatap.trace.mod.hook_service_patterns) 의 **공용 코어**. 여기엔 agent() 부팅이
699
- 없다 (데코레이터/부트 경로가 각자 처리).
906
+ (whatap.trace.mod.hook_service_patterns) 의 **공용 코어**. 여기엔 agent() 부팅이 없다
907
+ (데코레이터/부트 경로가 각자 처리).
700
908
 
701
909
  코루틴 함수는 동기 래퍼로 감싸면 트랜잭션이 start_interceptor → 코루틴 생성 →
702
910
  end_interceptor 로 즉시 열렸다 닫히고, 실제 본문은 호출자가 await 할 때 트랜잭션
@@ -772,26 +980,7 @@ def _service_trace(fn, service_name=None):
772
980
 
773
981
 
774
982
  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
983
+ trace = _service_trace(fn)
795
984
 
796
985
  if not os.environ.get('whatap.enabled'):
797
986
  agent()
@@ -966,6 +1155,22 @@ def get_port_number(port=6600, home=os.environ.get('WHATAP_HOME'), lock_file=Non
966
1155
 
967
1156
 
968
1157
  def configPort():
1158
+ if sys.platform.startswith('linux') and preview_whatap_conf('apm_enabled') == 'true':
1159
+ from whatap.util.native_process import acquire_start_lock, configured_channel, find_existing_agent
1160
+ try:
1161
+ spec = _native_agent_spec({})
1162
+ with acquire_start_lock(spec):
1163
+ if find_existing_agent(spec):
1164
+ # CLI allocation must not rewrite a live bootstrap agent's channel.
1165
+ return configured_channel(spec.home, spec.environment, include_unix=False)[1]
1166
+ return _config_port()
1167
+ except Exception as e:
1168
+ # 락 타임아웃/다중 인스턴스 등은 CLI 를 죽이지 말고 기존 포트 할당으로 폴백
1169
+ whatap_print('WHATAP: live agent check skipped, falling back to port file: %s' % e)
1170
+ return _config_port()
1171
+
1172
+
1173
+ def _config_port():
969
1174
  # force_net_udp_port가 설정된 경우 lock 파일 무시하고 해당 포트 강제 사용
970
1175
  force_port_str = preview_whatap_conf("force_net_udp_port")
971
1176
  if force_port_str and force_port_str not in ('false', 'False'):
@@ -0,0 +1,4 @@
1
+ app = 'Python'
2
+ name = 'whatap-python'
3
+ version = '2.2.1rc1'
4
+ release_date = '20260916'