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.
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/PKG-INFO +5 -1
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/setup.py +6 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/__init__.py +39 -21
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/agent/darwin/amd64/whatap_python +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/agent/darwin/arm64/whatap_python +0 -0
- whatap_python-2.0.2rc1/whatap/agent/linux/amd64/whatap_python +0 -0
- {whatap_python-2.0.0rc1/whatap/agent/linux/amd64 → whatap_python-2.0.2rc1/whatap/agent/linux/arm64}/whatap_python +0 -0
- whatap_python-2.0.2rc1/whatap/build.py +4 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/conf/configuration.py +2 -4
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/counter/__init__.py +6 -1
- whatap_python-2.0.2rc1/whatap/counter/tasks/__init__.py +3 -0
- whatap_python-2.0.2rc1/whatap/counter/tasks/llm_log_sink_task.py +242 -0
- whatap_python-2.0.2rc1/whatap/counter/tasks/llm_stat_task.py +77 -0
- whatap_python-2.0.2rc1/whatap/llm/__init__.py +1 -0
- whatap_python-2.0.2rc1/whatap/llm/definitions.py +43 -0
- whatap_python-2.0.2rc1/whatap/llm/log_sink_packs/__init__.py +8 -0
- whatap_python-2.0.2rc1/whatap/llm/log_sink_packs/llm_input_message.py +16 -0
- whatap_python-2.0.2rc1/whatap/llm/log_sink_packs/llm_log_sink_pack.py +64 -0
- whatap_python-2.0.2rc1/whatap/llm/log_sink_packs/llm_output_message.py +19 -0
- whatap_python-2.0.2rc1/whatap/llm/log_sink_packs/llm_step_status.py +114 -0
- whatap_python-2.0.2rc1/whatap/llm/log_sink_packs/llm_system_message.py +16 -0
- whatap_python-2.0.2rc1/whatap/llm/log_sink_packs/llm_tool_calls.py +44 -0
- whatap_python-2.0.2rc1/whatap/llm/log_sink_packs/llm_tool_results.py +16 -0
- whatap_python-2.0.2rc1/whatap/llm/log_sink_packs/llm_tx_status.py +102 -0
- whatap_python-2.0.2rc1/whatap/llm/pricing.py +236 -0
- whatap_python-2.0.2rc1/whatap/llm/providers/anthropic/__init__.py +37 -0
- whatap_python-2.0.2rc1/whatap/llm/providers/anthropic/messages/messages.py +67 -0
- whatap_python-2.0.2rc1/whatap/llm/providers/anthropic/messages/messages_context.py +76 -0
- whatap_python-2.0.2rc1/whatap/llm/providers/anthropic/messages/messages_extractor.py +123 -0
- whatap_python-2.0.2rc1/whatap/llm/providers/interceptor.py +85 -0
- {whatap_python-2.0.0rc1/whatap/trace/mod/llm → whatap_python-2.0.2rc1/whatap/llm/providers}/openai/__init__.py +33 -4
- whatap_python-2.0.2rc1/whatap/llm/providers/openai/chat/chat.py +79 -0
- whatap_python-2.0.2rc1/whatap/llm/providers/openai/chat/chat_context.py +78 -0
- whatap_python-2.0.2rc1/whatap/llm/providers/openai/chat/chat_extractor.py +126 -0
- whatap_python-2.0.2rc1/whatap/llm/providers/openai/completions/completions.py +67 -0
- whatap_python-2.0.2rc1/whatap/llm/providers/openai/completions/completions_context.py +32 -0
- whatap_python-2.0.2rc1/whatap/llm/providers/openai/completions/completions_extractor.py +61 -0
- 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
- whatap_python-2.0.2rc1/whatap/llm/providers/openai/embeddings/embeddings.py +54 -0
- whatap_python-2.0.2rc1/whatap/llm/providers/openai/embeddings/embeddings_context.py +26 -0
- whatap_python-2.0.2rc1/whatap/llm/providers/openai/embeddings/embeddings_extractor.py +26 -0
- whatap_python-2.0.2rc1/whatap/llm/providers/openai/responses/responses.py +67 -0
- whatap_python-2.0.2rc1/whatap/llm/providers/openai/responses/responses_context.py +88 -0
- whatap_python-2.0.2rc1/whatap/llm/providers/openai/responses/responses_extractor.py +125 -0
- whatap_python-2.0.2rc1/whatap/llm/providers/stream_accumulator.py +73 -0
- whatap_python-2.0.2rc1/whatap/llm/stats/__init__.py +15 -0
- 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
- whatap_python-2.0.2rc1/whatap/llm/stats/api_status_stat.py +28 -0
- 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
- whatap_python-2.0.2rc1/whatap/llm/stats/error_stat.py +56 -0
- whatap_python-2.0.2rc1/whatap/llm/stats/feature_stat.py +63 -0
- whatap_python-2.0.2rc1/whatap/llm/stats/meter.py +18 -0
- whatap_python-2.0.2rc1/whatap/llm/stats/perf_stat.py +105 -0
- whatap_python-2.0.2rc1/whatap/llm/stats/token_usage_stat.py +104 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/net/udp_session.py +29 -14
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/net/udp_thread.py +2 -4
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/scripts/__init__.py +6 -6
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/wsgi.py +4 -1
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/database/util.py +3 -11
- whatap_python-2.0.2rc1/whatap/trace/mod/email/__init__.py +0 -0
- whatap_python-2.0.2rc1/whatap/trace/mod/httpc/__init__.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/httpc/util.py +51 -33
- whatap_python-2.0.2rc1/whatap/trace/mod/standalone/__init__.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/standalone/multiple.py +4 -1
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/standalone/single.py +4 -1
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/trace_context.py +1 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/trace_context_manager.py +40 -1
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/trace_module_definition.py +2 -2
- whatap_python-2.0.2rc1/whatap/util/__init__.py +0 -0
- whatap_python-2.0.2rc1/whatap/util/cardinality/__init__.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap_python.egg-info/PKG-INFO +5 -1
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap_python.egg-info/SOURCES.txt +49 -16
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap_python.egg-info/requires.txt +4 -0
- whatap_python-2.0.0rc1/whatap/agent/linux/arm64/whatap_python +0 -0
- whatap_python-2.0.0rc1/whatap/build.py +0 -4
- whatap_python-2.0.0rc1/whatap/counter/tasks/__init__.py +0 -17
- whatap_python-2.0.0rc1/whatap/counter/tasks/llm_error_stat.py +0 -88
- whatap_python-2.0.0rc1/whatap/counter/tasks/llm_feature_stat.py +0 -91
- whatap_python-2.0.0rc1/whatap/counter/tasks/llm_meter.py +0 -57
- whatap_python-2.0.0rc1/whatap/counter/tasks/llm_perf_stat.py +0 -132
- whatap_python-2.0.0rc1/whatap/counter/tasks/llm_token_usage_stat.py +0 -122
- whatap_python-2.0.0rc1/whatap/trace/mod/llm/anthropic.py +0 -488
- whatap_python-2.0.0rc1/whatap/trace/mod/llm/llm_log_sink_pack.py +0 -316
- whatap_python-2.0.0rc1/whatap/trace/mod/llm/llm_sender.py +0 -527
- whatap_python-2.0.0rc1/whatap/trace/mod/llm/openai/chat.py +0 -356
- whatap_python-2.0.0rc1/whatap/trace/mod/llm/openai/embeddings.py +0 -178
- whatap_python-2.0.0rc1/whatap/trace/mod/llm/openai/responses.py +0 -409
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/README.md +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/pyproject.toml +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/setup.cfg +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/LICENSE +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/README.rst +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/__main__.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/agent/windows/whatap_python.exe +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/bootstrap/__init__.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/bootstrap/sitecustomize.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/conf/__init__.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/conf/configure.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/conf/license.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/control/__init__.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/counter/counter_manager.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/counter/tasks/base_task.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/counter/tasks/openfiledescriptor.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/io/__init__.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/io/data_inputx.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/io/data_outputx.py +0 -0
- {whatap_python-2.0.0rc1/whatap/net → whatap_python-2.0.2rc1/whatap/llm/providers}/__init__.py +0 -0
- {whatap_python-2.0.0rc1/whatap/pack → whatap_python-2.0.2rc1/whatap/llm/providers/anthropic/messages}/__init__.py +0 -0
- {whatap_python-2.0.0rc1/whatap/trace/mod → whatap_python-2.0.2rc1/whatap/llm/providers/openai/chat}/__init__.py +0 -0
- {whatap_python-2.0.0rc1/whatap/trace/mod/amqp → whatap_python-2.0.2rc1/whatap/llm/providers/openai/completions}/__init__.py +0 -0
- {whatap_python-2.0.0rc1/whatap/trace/mod/application → whatap_python-2.0.2rc1/whatap/llm/providers/openai/embeddings}/__init__.py +0 -0
- {whatap_python-2.0.0rc1/whatap/trace/mod/database → whatap_python-2.0.2rc1/whatap/llm/providers/openai/responses}/__init__.py +0 -0
- {whatap_python-2.0.0rc1/whatap/trace/mod/email → whatap_python-2.0.2rc1/whatap/net}/__init__.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/net/async_sender.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/net/packet_enum.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/net/packet_type_enum.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/net/param_def.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/net/stackhelper.py +0 -0
- {whatap_python-2.0.0rc1/whatap/trace/mod/httpc → whatap_python-2.0.2rc1/whatap/pack}/__init__.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/pack/logSinkPack.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/pack/pack.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/pack/pack_enum.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/pack/tagCountPack.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/__init__.py +0 -0
- {whatap_python-2.0.0rc1/whatap/trace/mod/llm → whatap_python-2.0.2rc1/whatap/trace/mod}/__init__.py +0 -0
- {whatap_python-2.0.0rc1/whatap/trace/mod/standalone → whatap_python-2.0.2rc1/whatap/trace/mod/amqp}/__init__.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/amqp/kombu.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/amqp/pika.py +0 -0
- {whatap_python-2.0.0rc1/whatap/util → whatap_python-2.0.2rc1/whatap/trace/mod/application}/__init__.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/bottle.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/celery.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/cherrypy.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/django.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/django_asgi.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/django_py3.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/fastapi.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/flask.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/frappe.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/graphql.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/nameko.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/odoo.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/starlette.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/application/tornado.py +0 -0
- {whatap_python-2.0.0rc1/whatap/util/cardinality → whatap_python-2.0.2rc1/whatap/trace/mod/database}/__init__.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/database/cxoracle.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/database/mongo.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/database/mysql.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/database/neo4j.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/database/psycopg2.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/database/psycopg3.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/database/redis.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/database/sqlalchemy.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/database/sqlite3.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/email/smtp.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/httpc/django.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/httpc/httplib.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/httpc/httpx.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/httpc/requests.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/httpc/urllib3.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/logging.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/mod/plugin.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/simple_trace_context.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/trace_error.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/trace_handler.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/trace/trace_import.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/bit_util.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/cardinality/hyperloglog.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/cardinality/murmurhash.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/cardinality/registerset.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/compare_util.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/date_util.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/debug_util.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/escape_literal_sql.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/frame_util.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/hash_util.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/hexa32.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/int_set.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/ip_util.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/keygen.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/linked_list.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/linked_map.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/metering_util.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/request_double_queue.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/request_queue.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/string_util.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/throttle_util.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/util/userid_util.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/__init__.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/blob_value.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/boolean_value.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/decimal_value.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/double_summary.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/double_value.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/float_array.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/float_value.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/int_array.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/ip4_value.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/list_value.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/long_array.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/long_summary.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/map_value.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/null_value.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/number_value.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/summary_value.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/text_array.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/text_hash_value.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/text_value.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/value.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/value/value_enum.py +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap/whatap.conf +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap_python.egg-info/dependency_links.txt +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap_python.egg-info/entry_points.txt +0 -0
- {whatap_python-2.0.0rc1 → whatap_python-2.0.2rc1}/whatap_python.egg-info/not-zip-safe +0 -0
- {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.
|
|
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 =
|
|
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
|
-
|
|
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)
|
|
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':
|
|
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__))
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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":
|
|
226
|
-
"
|
|
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,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 ''
|