whatap-python 2.1.0__py3-none-any.whl
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/LICENSE +0 -0
- whatap/README.rst +49 -0
- whatap/__init__.py +923 -0
- whatap/__main__.py +4 -0
- whatap/agent/darwin/amd64/whatap_python +0 -0
- whatap/agent/darwin/arm64/whatap_python +0 -0
- whatap/agent/linux/amd64/whatap_python +0 -0
- whatap/agent/linux/arm64/whatap_python +0 -0
- whatap/agent/windows/whatap_python.exe +0 -0
- whatap/bootstrap/__init__.py +0 -0
- whatap/bootstrap/sitecustomize.py +19 -0
- whatap/build.py +4 -0
- whatap/conf/__init__.py +0 -0
- whatap/conf/configuration.py +280 -0
- whatap/conf/configure.py +105 -0
- whatap/conf/license.py +49 -0
- whatap/control/__init__.py +0 -0
- whatap/counter/__init__.py +14 -0
- whatap/counter/counter_manager.py +45 -0
- whatap/counter/tasks/__init__.py +3 -0
- whatap/counter/tasks/base_task.py +26 -0
- whatap/counter/tasks/llm_evaluator_task.py +501 -0
- whatap/counter/tasks/llm_log_sink_task.py +309 -0
- whatap/counter/tasks/llm_stat_task.py +78 -0
- whatap/counter/tasks/openfiledescriptor.py +67 -0
- whatap/io/__init__.py +1 -0
- whatap/io/data_inputx.py +161 -0
- whatap/io/data_outputx.py +262 -0
- whatap/llm/__init__.py +17 -0
- whatap/llm/definitions.py +43 -0
- whatap/llm/evaluators/__init__.py +136 -0
- whatap/llm/evaluators/base.py +114 -0
- whatap/llm/evaluators/builtins/__init__.py +91 -0
- whatap/llm/evaluators/builtins/answer_relevance.py +46 -0
- whatap/llm/evaluators/builtins/combined_judge.py +271 -0
- whatap/llm/evaluators/builtins/factuality.py +71 -0
- whatap/llm/evaluators/builtins/hallucination.py +97 -0
- whatap/llm/evaluators/builtins/llm_judge.py +516 -0
- whatap/llm/evaluators/builtins/pii_leak.py +214 -0
- whatap/llm/evaluators/builtins/prompt_injection.py +71 -0
- whatap/llm/evaluators/builtins/toxicity.py +53 -0
- whatap/llm/evaluators/builtins/url_scan.py +194 -0
- whatap/llm/evaluators/registry.py +192 -0
- whatap/llm/evaluators/sampler.py +83 -0
- whatap/llm/evaluators/scope.py +334 -0
- whatap/llm/features.py +66 -0
- whatap/llm/log_sink_packs/__init__.py +9 -0
- whatap/llm/log_sink_packs/llm_input_message.py +16 -0
- whatap/llm/log_sink_packs/llm_log_sink_pack.py +72 -0
- whatap/llm/log_sink_packs/llm_output_message.py +19 -0
- whatap/llm/log_sink_packs/llm_step_eval_status.py +94 -0
- whatap/llm/log_sink_packs/llm_step_status.py +118 -0
- whatap/llm/log_sink_packs/llm_system_message.py +16 -0
- whatap/llm/log_sink_packs/llm_tool_calls.py +44 -0
- whatap/llm/log_sink_packs/llm_tool_results.py +16 -0
- whatap/llm/log_sink_packs/llm_tx_status.py +108 -0
- whatap/llm/pricing.py +236 -0
- whatap/llm/prompt_meta.py +288 -0
- whatap/llm/providers/__init__.py +0 -0
- whatap/llm/providers/anthropic/__init__.py +37 -0
- whatap/llm/providers/anthropic/messages/__init__.py +0 -0
- whatap/llm/providers/anthropic/messages/messages.py +70 -0
- whatap/llm/providers/anthropic/messages/messages_context.py +76 -0
- whatap/llm/providers/anthropic/messages/messages_extractor.py +126 -0
- whatap/llm/providers/interceptor.py +182 -0
- whatap/llm/providers/openai/__init__.py +133 -0
- whatap/llm/providers/openai/chat/__init__.py +0 -0
- whatap/llm/providers/openai/chat/chat.py +82 -0
- whatap/llm/providers/openai/chat/chat_context.py +78 -0
- whatap/llm/providers/openai/chat/chat_extractor.py +127 -0
- whatap/llm/providers/openai/completions/__init__.py +0 -0
- whatap/llm/providers/openai/completions/completions.py +70 -0
- whatap/llm/providers/openai/completions/completions_context.py +31 -0
- whatap/llm/providers/openai/completions/completions_extractor.py +61 -0
- whatap/llm/providers/openai/content_parser.py +41 -0
- whatap/llm/providers/openai/embeddings/__init__.py +0 -0
- whatap/llm/providers/openai/embeddings/embeddings.py +59 -0
- whatap/llm/providers/openai/embeddings/embeddings_context.py +25 -0
- whatap/llm/providers/openai/embeddings/embeddings_extractor.py +26 -0
- whatap/llm/providers/openai/responses/__init__.py +0 -0
- whatap/llm/providers/openai/responses/responses.py +70 -0
- whatap/llm/providers/openai/responses/responses_context.py +88 -0
- whatap/llm/providers/openai/responses/responses_extractor.py +126 -0
- whatap/llm/providers/stream_accumulator.py +73 -0
- whatap/llm/stats/__init__.py +35 -0
- whatap/llm/stats/active_stat.py +86 -0
- whatap/llm/stats/answer_relevance_eval_stat.py +10 -0
- whatap/llm/stats/api_status_stat.py +35 -0
- whatap/llm/stats/base_stat.py +107 -0
- whatap/llm/stats/combined_judge_eval_stat.py +11 -0
- whatap/llm/stats/error_stat.py +59 -0
- whatap/llm/stats/eval_stat.py +225 -0
- whatap/llm/stats/factuality_eval_stat.py +10 -0
- whatap/llm/stats/feature_stat.py +104 -0
- whatap/llm/stats/finish_stat.py +105 -0
- whatap/llm/stats/hallucination_eval_stat.py +10 -0
- whatap/llm/stats/meter.py +18 -0
- whatap/llm/stats/perf_stat.py +117 -0
- whatap/llm/stats/pii_leak_eval_stat.py +12 -0
- whatap/llm/stats/prompt_injection_eval_stat.py +10 -0
- whatap/llm/stats/token_usage_stat.py +133 -0
- whatap/llm/stats/toxicity_eval_stat.py +10 -0
- whatap/llm/stats/url_scan_eval_stat.py +12 -0
- whatap/net/__init__.py +0 -0
- whatap/net/async_sender.py +107 -0
- whatap/net/packet_enum.py +44 -0
- whatap/net/packet_type_enum.py +31 -0
- whatap/net/param_def.py +69 -0
- whatap/net/stackhelper.py +87 -0
- whatap/net/udp_session.py +394 -0
- whatap/net/udp_thread.py +54 -0
- whatap/pack/__init__.py +0 -0
- whatap/pack/logSinkPack.py +77 -0
- whatap/pack/pack.py +34 -0
- whatap/pack/pack_enum.py +41 -0
- whatap/pack/tagCountPack.py +61 -0
- whatap/scripts/__init__.py +208 -0
- whatap/trace/__init__.py +12 -0
- whatap/trace/mod/__init__.py +0 -0
- whatap/trace/mod/amqp/__init__.py +0 -0
- whatap/trace/mod/amqp/kombu.py +122 -0
- whatap/trace/mod/amqp/pika.py +62 -0
- whatap/trace/mod/application/__init__.py +0 -0
- whatap/trace/mod/application/bottle.py +34 -0
- whatap/trace/mod/application/celery.py +81 -0
- whatap/trace/mod/application/cherrypy.py +30 -0
- whatap/trace/mod/application/django.py +287 -0
- whatap/trace/mod/application/django_asgi.py +266 -0
- whatap/trace/mod/application/django_py3.py +251 -0
- whatap/trace/mod/application/fastapi/__init__.py +31 -0
- whatap/trace/mod/application/fastapi/endpoint.py +73 -0
- whatap/trace/mod/application/fastapi/exception_log.py +63 -0
- whatap/trace/mod/application/fastapi/instrumentation.py +204 -0
- whatap/trace/mod/application/fastapi/scope.py +115 -0
- whatap/trace/mod/application/fastapi/transaction.py +67 -0
- whatap/trace/mod/application/flask.py +52 -0
- whatap/trace/mod/application/frappe.py +224 -0
- whatap/trace/mod/application/graphql.py +170 -0
- whatap/trace/mod/application/nameko.py +39 -0
- whatap/trace/mod/application/odoo.py +63 -0
- whatap/trace/mod/application/starlette.py +126 -0
- whatap/trace/mod/application/tornado.py +163 -0
- whatap/trace/mod/application/wsgi.py +195 -0
- whatap/trace/mod/database/__init__.py +0 -0
- whatap/trace/mod/database/cxoracle.py +49 -0
- whatap/trace/mod/database/mongo.py +169 -0
- whatap/trace/mod/database/mysql.py +80 -0
- whatap/trace/mod/database/neo4j.py +90 -0
- whatap/trace/mod/database/psycopg2.py +45 -0
- whatap/trace/mod/database/psycopg3.py +359 -0
- whatap/trace/mod/database/redis.py +122 -0
- whatap/trace/mod/database/sqlalchemy.py +213 -0
- whatap/trace/mod/database/sqlite3.py +130 -0
- whatap/trace/mod/database/util.py +630 -0
- whatap/trace/mod/email/__init__.py +0 -0
- whatap/trace/mod/email/smtp.py +78 -0
- whatap/trace/mod/httpc/__init__.py +0 -0
- whatap/trace/mod/httpc/django.py +31 -0
- whatap/trace/mod/httpc/httplib.py +70 -0
- whatap/trace/mod/httpc/httpx.py +62 -0
- whatap/trace/mod/httpc/requests.py +20 -0
- whatap/trace/mod/httpc/urllib3.py +27 -0
- whatap/trace/mod/httpc/util.py +388 -0
- whatap/trace/mod/logging.py +161 -0
- whatap/trace/mod/plugin.py +84 -0
- whatap/trace/mod/standalone/__init__.py +0 -0
- whatap/trace/mod/standalone/multiple.py +293 -0
- whatap/trace/mod/standalone/single.py +135 -0
- whatap/trace/simple_trace_context.py +18 -0
- whatap/trace/trace_context.py +212 -0
- whatap/trace/trace_context_manager.py +244 -0
- whatap/trace/trace_error.py +84 -0
- whatap/trace/trace_handler.py +89 -0
- whatap/trace/trace_import.py +91 -0
- whatap/trace/trace_module_definition.py +156 -0
- whatap/util/__init__.py +0 -0
- whatap/util/bit_util.py +49 -0
- whatap/util/cardinality/__init__.py +0 -0
- whatap/util/cardinality/hyperloglog.py +84 -0
- whatap/util/cardinality/murmurhash.py +20 -0
- whatap/util/cardinality/registerset.py +60 -0
- whatap/util/compare_util.py +19 -0
- whatap/util/date_util.py +55 -0
- whatap/util/debug_util.py +73 -0
- whatap/util/escape_literal_sql.py +233 -0
- whatap/util/frame_util.py +20 -0
- whatap/util/hash_util.py +103 -0
- whatap/util/hexa32.py +66 -0
- whatap/util/int_set.py +199 -0
- whatap/util/ip_util.py +63 -0
- whatap/util/keygen.py +11 -0
- whatap/util/linked_list.py +113 -0
- whatap/util/linked_map.py +359 -0
- whatap/util/metering_util.py +103 -0
- whatap/util/request_double_queue.py +68 -0
- whatap/util/request_queue.py +60 -0
- whatap/util/string_util.py +20 -0
- whatap/util/throttle_util.py +99 -0
- whatap/util/userid_util.py +134 -0
- whatap/value/__init__.py +1 -0
- whatap/value/blob_value.py +38 -0
- whatap/value/boolean_value.py +33 -0
- whatap/value/decimal_value.py +36 -0
- whatap/value/double_summary.py +86 -0
- whatap/value/double_value.py +33 -0
- whatap/value/float_array.py +42 -0
- whatap/value/float_value.py +34 -0
- whatap/value/int_array.py +42 -0
- whatap/value/ip4_value.py +50 -0
- whatap/value/list_value.py +105 -0
- whatap/value/long_array.py +44 -0
- whatap/value/long_summary.py +83 -0
- whatap/value/map_value.py +154 -0
- whatap/value/null_value.py +21 -0
- whatap/value/number_value.py +33 -0
- whatap/value/summary_value.py +39 -0
- whatap/value/text_array.py +58 -0
- whatap/value/text_hash_value.py +37 -0
- whatap/value/text_value.py +43 -0
- whatap/value/value.py +26 -0
- whatap/value/value_enum.py +80 -0
- whatap/whatap.conf +14 -0
- whatap_python-2.1.0.dist-info/METADATA +87 -0
- whatap_python-2.1.0.dist-info/RECORD +227 -0
- whatap_python-2.1.0.dist-info/WHEEL +5 -0
- whatap_python-2.1.0.dist-info/entry_points.txt +6 -0
- whatap_python-2.1.0.dist-info/top_level.txt +1 -0
|
File without changes
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from whatap.trace.trace_handler import trace_handler
|
|
2
|
+
from whatap.trace.mod.httpc.util import transfer, \
|
|
3
|
+
interceptor_httpc_request
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def instrument_revproxy_views(module):
|
|
7
|
+
def wrapper(fn):
|
|
8
|
+
@trace_handler(fn)
|
|
9
|
+
def trace(*args, **kwargs):
|
|
10
|
+
callback = fn(*args, **kwargs)
|
|
11
|
+
|
|
12
|
+
# set mtid header
|
|
13
|
+
callback = transfer(callback)
|
|
14
|
+
return callback
|
|
15
|
+
return trace
|
|
16
|
+
|
|
17
|
+
module.ProxyView.get_proxy_request_headers = wrapper(module.ProxyView.get_proxy_request_headers)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def wrapper(fn):
|
|
21
|
+
@trace_handler(fn)
|
|
22
|
+
def trace(*args, **kwargs):
|
|
23
|
+
# set httpc_url
|
|
24
|
+
httpc_url = args[0].upstream
|
|
25
|
+
callback = interceptor_httpc_request(fn, httpc_url, *args, **kwargs)
|
|
26
|
+
return callback
|
|
27
|
+
|
|
28
|
+
return trace
|
|
29
|
+
|
|
30
|
+
module.ProxyView.dispatch = wrapper(module.ProxyView.dispatch)
|
|
31
|
+
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from whatap.trace.trace_handler import trace_handler
|
|
2
|
+
from whatap.trace.mod.httpc.util import transfer, \
|
|
3
|
+
interceptor_httpc_request, interceptor_sock_connect
|
|
4
|
+
|
|
5
|
+
request_injection_processed = False
|
|
6
|
+
def instrument_httplib(module):
|
|
7
|
+
global request_injection_processed
|
|
8
|
+
def wrapper(fn):
|
|
9
|
+
@trace_handler(fn)
|
|
10
|
+
def trace(*args, **kwargs):
|
|
11
|
+
# set mtid header
|
|
12
|
+
kwargs['headers'] = transfer(kwargs.get('headers', {}))
|
|
13
|
+
newargs = []
|
|
14
|
+
for arg in args:
|
|
15
|
+
if arg and isinstance(arg, dict) and 'host' in [x.lower() for x in arg.keys()]:
|
|
16
|
+
arg = transfer(arg)
|
|
17
|
+
newargs.append(arg)
|
|
18
|
+
# set httpc_url
|
|
19
|
+
httpc_url = args[2]
|
|
20
|
+
if hasattr(args[0], 'host'):
|
|
21
|
+
httpc_url = getattr(args[0], 'host') + httpc_url
|
|
22
|
+
callback = interceptor_httpc_request(fn, httpc_url, *newargs, **kwargs)
|
|
23
|
+
return callback
|
|
24
|
+
|
|
25
|
+
return trace
|
|
26
|
+
if not request_injection_processed:
|
|
27
|
+
module.HTTPConnection.request = wrapper(module.HTTPConnection.request)
|
|
28
|
+
request_injection_processed = True
|
|
29
|
+
|
|
30
|
+
def wrapper(fn):
|
|
31
|
+
@trace_handler(fn)
|
|
32
|
+
def trace(*args, **kwargs):
|
|
33
|
+
callback = interceptor_sock_connect(fn, *args, **kwargs)
|
|
34
|
+
return callback
|
|
35
|
+
|
|
36
|
+
return trace
|
|
37
|
+
module.HTTPConnection.connect = wrapper(module.HTTPConnection.connect)
|
|
38
|
+
|
|
39
|
+
def instrument_httplib2(module):
|
|
40
|
+
global request_injection_processed
|
|
41
|
+
def wrapper(fn):
|
|
42
|
+
@trace_handler(fn)
|
|
43
|
+
def trace(*args, **kwargs):
|
|
44
|
+
# set mtid header
|
|
45
|
+
kwargs['headers'] = transfer(kwargs.get('headers', {}))
|
|
46
|
+
newargs = []
|
|
47
|
+
for arg in args:
|
|
48
|
+
if arg and isinstance(arg, dict) and 'host' in [x.lower() for x in arg.keys()]:
|
|
49
|
+
arg = transfer(arg)
|
|
50
|
+
newargs.append(arg)
|
|
51
|
+
# set httpc_url
|
|
52
|
+
httpc_url = args[2]
|
|
53
|
+
if hasattr(args[0], 'host'):
|
|
54
|
+
httpc_url = getattr(args[0], 'host') + httpc_url
|
|
55
|
+
callback = interceptor_httpc_request(fn, httpc_url, *newargs, **kwargs)
|
|
56
|
+
return callback
|
|
57
|
+
|
|
58
|
+
return trace
|
|
59
|
+
if not request_injection_processed:
|
|
60
|
+
module.Http.request = wrapper(module.Http.request)
|
|
61
|
+
request_injection_processed = True
|
|
62
|
+
def wrapper(fn):
|
|
63
|
+
@trace_handler(fn)
|
|
64
|
+
def trace(*args, **kwargs):
|
|
65
|
+
callback = interceptor_sock_connect(fn, *args, **kwargs)
|
|
66
|
+
return callback
|
|
67
|
+
return trace
|
|
68
|
+
|
|
69
|
+
module.HTTPConnectionWithTimeout.connect = wrapper(module.HTTPConnectionWithTimeout.connect)
|
|
70
|
+
module.HTTPSConnectionWithTimeout.connect = wrapper(module.HTTPSConnectionWithTimeout.connect)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from whatap.trace.trace_handler import trace_handler, async_trace_handler
|
|
2
|
+
from whatap.trace.mod.httpc.util import transfer, \
|
|
3
|
+
interceptor_httpc_request, async_interceptor_httpc_request
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def instrument_httpx(module):
|
|
7
|
+
def wrapper(fn):
|
|
8
|
+
@trace_handler(fn)
|
|
9
|
+
def trace(*args, **kwargs):
|
|
10
|
+
if len(args) >= 2 and hasattr(args[1], 'headers') and hasattr(args[1], 'url'):
|
|
11
|
+
request = args[1]
|
|
12
|
+
request.headers = transfer(request.headers)
|
|
13
|
+
httpc_url = str(request.url)
|
|
14
|
+
|
|
15
|
+
# 2. stream call: send(request=request, ...)
|
|
16
|
+
elif len(args) == 1 and 'request' in kwargs:
|
|
17
|
+
request = kwargs['request']
|
|
18
|
+
if hasattr(request, 'headers') and hasattr(request, 'url'):
|
|
19
|
+
request.headers = transfer(request.headers)
|
|
20
|
+
httpc_url = str(request.url)
|
|
21
|
+
else:
|
|
22
|
+
httpc_url = "invalid_request_object"
|
|
23
|
+
|
|
24
|
+
# 3. 예상치 못한 패턴
|
|
25
|
+
else:
|
|
26
|
+
httpc_url = "httpx_unknown_pattern"
|
|
27
|
+
|
|
28
|
+
return interceptor_httpc_request(fn, httpc_url, *args, **kwargs)
|
|
29
|
+
|
|
30
|
+
return trace
|
|
31
|
+
|
|
32
|
+
def async_wrapper(fn):
|
|
33
|
+
@async_trace_handler(fn)
|
|
34
|
+
async def trace(*args, **kwargs):
|
|
35
|
+
if len(args) >= 2 and hasattr(args[1], 'headers') and hasattr(args[1], 'url'):
|
|
36
|
+
request = args[1]
|
|
37
|
+
request.headers = transfer(request.headers)
|
|
38
|
+
httpc_url = str(request.url)
|
|
39
|
+
|
|
40
|
+
# 2. stream call: send(request=request, ...)
|
|
41
|
+
elif len(args) == 1 and 'request' in kwargs:
|
|
42
|
+
request = kwargs['request']
|
|
43
|
+
if hasattr(request, 'headers') and hasattr(request, 'url'):
|
|
44
|
+
request.headers = transfer(request.headers)
|
|
45
|
+
httpc_url = str(request.url)
|
|
46
|
+
else:
|
|
47
|
+
httpc_url = "invalid_request_object"
|
|
48
|
+
|
|
49
|
+
# 3. 예상치 못한 패턴
|
|
50
|
+
else:
|
|
51
|
+
httpc_url = "httpx_unknown_pattern"
|
|
52
|
+
|
|
53
|
+
return await async_interceptor_httpc_request(fn, httpc_url, *args, **kwargs)
|
|
54
|
+
|
|
55
|
+
return trace
|
|
56
|
+
|
|
57
|
+
if hasattr(module, 'Client') and hasattr(module.Client, 'send'):
|
|
58
|
+
module.Client.send = wrapper(module.Client.send)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if hasattr(module, 'AsyncClient') and hasattr(module.AsyncClient, 'send'):
|
|
62
|
+
module.AsyncClient.send = async_wrapper(module.AsyncClient.send)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from whatap.trace.trace_handler import trace_handler
|
|
2
|
+
from whatap.trace.mod.httpc.util import transfer, \
|
|
3
|
+
interceptor_httpc_request
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def instrument_requests(module):
|
|
7
|
+
def wrapper(fn):
|
|
8
|
+
@trace_handler(fn)
|
|
9
|
+
def trace(*args, **kwargs):
|
|
10
|
+
# set mtid header
|
|
11
|
+
args[1].headers = transfer(args[1].headers)
|
|
12
|
+
|
|
13
|
+
# set httpc_url
|
|
14
|
+
httpc_url = args[1].url
|
|
15
|
+
callback = interceptor_httpc_request(fn, httpc_url, *args, **kwargs)
|
|
16
|
+
return callback
|
|
17
|
+
|
|
18
|
+
return trace
|
|
19
|
+
|
|
20
|
+
module.Session.send = wrapper(module.Session.send)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from whatap.trace.trace_handler import trace_handler
|
|
2
|
+
from whatap.trace.mod.httpc.util import transfer, interceptor_httpc_request, \
|
|
3
|
+
interceptor_sock_connect
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def instrument_urllib3(module):
|
|
7
|
+
def wrapper(fn):
|
|
8
|
+
@trace_handler(fn)
|
|
9
|
+
def trace(*args, **kwargs):
|
|
10
|
+
# set mtid header
|
|
11
|
+
kwargs['headers'] = transfer(kwargs.get('headers', {}))
|
|
12
|
+
newargs = []
|
|
13
|
+
for arg in args:
|
|
14
|
+
if arg and isinstance(arg, dict) and 'host' in [x.lower() for x in arg.keys()]:
|
|
15
|
+
arg = transfer(arg)
|
|
16
|
+
newargs.append(arg)
|
|
17
|
+
# set httpc_url
|
|
18
|
+
httpc_url = args[2]
|
|
19
|
+
if hasattr(args[0], 'host'):
|
|
20
|
+
httpc_url = getattr(args[0], 'host') + httpc_url
|
|
21
|
+
callback = interceptor_httpc_request(fn, httpc_url, *newargs, **kwargs)
|
|
22
|
+
return callback
|
|
23
|
+
|
|
24
|
+
return trace
|
|
25
|
+
if hasattr(module, 'RequestMethods'):
|
|
26
|
+
module.RequestMethods.request = wrapper(module.RequestMethods.request)
|
|
27
|
+
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import random
|
|
2
|
+
|
|
3
|
+
from whatap import logging
|
|
4
|
+
from whatap.conf.configure import Configure as conf
|
|
5
|
+
from whatap.net.packet_type_enum import PacketTypeEnum
|
|
6
|
+
import whatap.net.async_sender as async_sender
|
|
7
|
+
from whatap.trace.trace_context_manager import TraceContextManager
|
|
8
|
+
from whatap.trace.trace_error import interceptor_step_error
|
|
9
|
+
from whatap.llm.definitions import URL_OPERATION_MAP
|
|
10
|
+
from whatap.util.date_util import DateUtil
|
|
11
|
+
from whatap.util.hexa32 import Hexa32
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _update_api_status_stats(ctx, status_code):
|
|
15
|
+
try:
|
|
16
|
+
from whatap.counter.tasks.llm_stat_task import LlmStatTask
|
|
17
|
+
stat = LlmStatTask.get_stat('ApiStatusStat')
|
|
18
|
+
if not stat:
|
|
19
|
+
return
|
|
20
|
+
httpc_url = getattr(ctx, '_llm_httpc_url', '') or ''
|
|
21
|
+
url = ''
|
|
22
|
+
if httpc_url:
|
|
23
|
+
if '://' in httpc_url:
|
|
24
|
+
httpc_url = httpc_url.split('://', 1)[1]
|
|
25
|
+
if '/' in httpc_url:
|
|
26
|
+
url = '/' + httpc_url.split('/', 1)[1]
|
|
27
|
+
# operation_type / prompt_version 은 prompt_meta scope 우선 (사용자 라벨링).
|
|
28
|
+
# scope 미적용 시 default ('default', 'v1').
|
|
29
|
+
try:
|
|
30
|
+
from whatap.llm.prompt_meta import get_prompt_meta
|
|
31
|
+
op_type, prompt_version = get_prompt_meta()
|
|
32
|
+
except Exception:
|
|
33
|
+
op_type = getattr(ctx, '_llm_operation_type', None) or 'unknown'
|
|
34
|
+
prompt_version = 'v1'
|
|
35
|
+
stat.update_stats(
|
|
36
|
+
getattr(ctx, '_llm_model', None) or 'unknown',
|
|
37
|
+
getattr(ctx, '_llm_provider', None) or '',
|
|
38
|
+
op_type,
|
|
39
|
+
status_code,
|
|
40
|
+
url=url,
|
|
41
|
+
prompt_version=prompt_version,
|
|
42
|
+
)
|
|
43
|
+
except Exception:
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _extract_host(url):
|
|
48
|
+
"""URL에서 호스트 추출"""
|
|
49
|
+
host = url
|
|
50
|
+
if '://' in host:
|
|
51
|
+
host = host.split('://', 1)[1]
|
|
52
|
+
return host.split('/', 1)[0]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _match_llm_api_host(httpc_url):
|
|
56
|
+
"""whatap.conf의 llm_api_hosts 설정과 호스트 매칭"""
|
|
57
|
+
hosts = conf.llm_api_hosts
|
|
58
|
+
if not hosts:
|
|
59
|
+
return False
|
|
60
|
+
try:
|
|
61
|
+
host = _extract_host(httpc_url)
|
|
62
|
+
if not host:
|
|
63
|
+
return False
|
|
64
|
+
if isinstance(hosts, str):
|
|
65
|
+
hosts = [h.strip() for h in hosts.split(',') if h.strip()]
|
|
66
|
+
for h in hosts:
|
|
67
|
+
if host == h or host.endswith('.' + h):
|
|
68
|
+
return True
|
|
69
|
+
except Exception:
|
|
70
|
+
pass
|
|
71
|
+
return False
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _detect_llm_api(ctx, httpc_url):
|
|
75
|
+
"""LLM API 감지: 플래그 기반, URL 패턴 기반, 호스트 설정 기반"""
|
|
76
|
+
op_type = 'unknown'
|
|
77
|
+
for pattern, ot in URL_OPERATION_MAP:
|
|
78
|
+
if pattern in httpc_url:
|
|
79
|
+
op_type = ot
|
|
80
|
+
break
|
|
81
|
+
if getattr(ctx, '_llm_httpc_pending', False) and (op_type != 'unknown' or _match_llm_api_host(httpc_url)):
|
|
82
|
+
ctx.driver = 'LLM API'
|
|
83
|
+
ctx.is_llm = 1
|
|
84
|
+
ctx._llm_httpc_pending = False
|
|
85
|
+
ctx._llm_operation_type = op_type
|
|
86
|
+
ctx._llm_httpc_url = httpc_url
|
|
87
|
+
|
|
88
|
+
# HTTPC URL에서 호스트/경로 추출 → ctx 속성 + active stat 즉시 등록
|
|
89
|
+
try:
|
|
90
|
+
host = _extract_host(httpc_url)
|
|
91
|
+
url_path = ''
|
|
92
|
+
raw = httpc_url
|
|
93
|
+
if '://' in raw:
|
|
94
|
+
raw = raw.split('://', 1)[1]
|
|
95
|
+
if '/' in raw:
|
|
96
|
+
url_path = '/' + raw.split('/', 1)[1]
|
|
97
|
+
if host:
|
|
98
|
+
ctx._llm_provider = host
|
|
99
|
+
model = getattr(ctx, '_llm_model', None)
|
|
100
|
+
if model:
|
|
101
|
+
from whatap.counter.tasks.llm_stat_task import LlmStatTask
|
|
102
|
+
stat = LlmStatTask.get_stat('ActiveStat')
|
|
103
|
+
if stat:
|
|
104
|
+
stat.set_host(model, host, url_path)
|
|
105
|
+
except Exception:
|
|
106
|
+
pass
|
|
107
|
+
return True
|
|
108
|
+
return False
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def interceptor_httpc_request(fn, httpc_url, *args, **kwargs):
|
|
112
|
+
ctx = TraceContextManager.getLocalContext()
|
|
113
|
+
if not ctx or ctx.active_httpc_hash:
|
|
114
|
+
try:
|
|
115
|
+
return fn(*args, **kwargs)
|
|
116
|
+
except Exception as e:
|
|
117
|
+
interceptor_step_error(e, ctx=ctx)
|
|
118
|
+
raise
|
|
119
|
+
|
|
120
|
+
param = None
|
|
121
|
+
method = None
|
|
122
|
+
if httpc_url.find('?') > -1:
|
|
123
|
+
httpc_url, param = httpc_url.split('?')
|
|
124
|
+
|
|
125
|
+
is_llm = _detect_llm_api(ctx, httpc_url)
|
|
126
|
+
if not ctx.step_id:
|
|
127
|
+
seq = getattr(ctx, '_httpc_seq', 1)
|
|
128
|
+
ctx.step_id = seq
|
|
129
|
+
ctx._httpc_seq = seq + 1
|
|
130
|
+
|
|
131
|
+
start_time = DateUtil.nowSystem()
|
|
132
|
+
ctx.start_time = start_time
|
|
133
|
+
ctx.httpc_url = httpc_url
|
|
134
|
+
ctx.active_httpc_hash = ctx.httpc_url
|
|
135
|
+
ctx.active_httpc_start_time = DateUtil.nowSystem()
|
|
136
|
+
|
|
137
|
+
callback = None
|
|
138
|
+
try:
|
|
139
|
+
try:
|
|
140
|
+
callback = fn(*args, **kwargs)
|
|
141
|
+
return callback
|
|
142
|
+
except TypeError as e:
|
|
143
|
+
callback = fn(*args)
|
|
144
|
+
return callback
|
|
145
|
+
except Exception as e:
|
|
146
|
+
interceptor_step_error(e, ctx=ctx)
|
|
147
|
+
raise
|
|
148
|
+
finally:
|
|
149
|
+
try:
|
|
150
|
+
if ctx.driver == 'LLM API' and callback is not None:
|
|
151
|
+
status_code = getattr(callback, 'status_code', None)
|
|
152
|
+
if status_code:
|
|
153
|
+
_update_api_status_stats(ctx, status_code)
|
|
154
|
+
if status_code >= 400:
|
|
155
|
+
try:
|
|
156
|
+
read_fn = getattr(callback, 'read', None)
|
|
157
|
+
if callable(read_fn):
|
|
158
|
+
try:
|
|
159
|
+
read_fn()
|
|
160
|
+
except Exception:
|
|
161
|
+
pass
|
|
162
|
+
error_text = callback.text or ''
|
|
163
|
+
except Exception as e:
|
|
164
|
+
logging.warning('[HTTPC] LLM API error text read failed: %s' % e, extra={'id': 'HTTPC001'})
|
|
165
|
+
error_text = ''
|
|
166
|
+
try:
|
|
167
|
+
interceptor_step_error(
|
|
168
|
+
Exception('[LLM API Error] HTTP {} {}'.format(status_code, error_text)),
|
|
169
|
+
ctx=ctx
|
|
170
|
+
)
|
|
171
|
+
except Exception as e:
|
|
172
|
+
logging.warning('[HTTPC] interceptor_step_error failed: %s' % e, extra={'id': 'HTTPC002'})
|
|
173
|
+
except Exception as e:
|
|
174
|
+
logging.warning('[HTTPC] LLM API error handling failed: %s' % e, extra={'id': 'HTTPC003'})
|
|
175
|
+
|
|
176
|
+
datas = [ctx.httpc_url, ctx.step_id, ctx.driver]
|
|
177
|
+
ctx.elapsed = DateUtil.nowSystem() - start_time
|
|
178
|
+
async_sender.send_packet(PacketTypeEnum.TX_HTTPC, ctx, datas)
|
|
179
|
+
|
|
180
|
+
if conf.profile_http_parameter_enabled and len(args) > 1:
|
|
181
|
+
try:
|
|
182
|
+
if type(args[1]) == dict:
|
|
183
|
+
param = (args[1].body if 'body' in args[1] else args[1]) or param
|
|
184
|
+
method = args[1].method if 'method' in args[1] else args[1]
|
|
185
|
+
|
|
186
|
+
if param:
|
|
187
|
+
datas = ['HTTP-PARAMETERS', method, param]
|
|
188
|
+
ctx.start_time = DateUtil.nowSystem()
|
|
189
|
+
async_sender.send_packet(PacketTypeEnum.TX_SECURE_MSG, ctx, datas)
|
|
190
|
+
except Exception as _e:
|
|
191
|
+
# finally 가 어떤 일이 있어도 return 값을 exception 으로 둔갑시키지 않도록 방어
|
|
192
|
+
logging.warning(
|
|
193
|
+
'[HTTPC] profile_http_parameter_enabled extract failed: %s' % _e,
|
|
194
|
+
extra={'id': 'HTTPC004'}
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
if is_llm:
|
|
198
|
+
ctx._llm_step_id = ctx.step_id
|
|
199
|
+
ctx.active_httpc_hash = 0
|
|
200
|
+
ctx.active_httpc_start_time = 0
|
|
201
|
+
ctx.httpc_url = None
|
|
202
|
+
ctx.step_id = 0
|
|
203
|
+
ctx.driver = ''
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
async def async_interceptor_httpc_request(fn, httpc_url, *args, **kwargs):
|
|
207
|
+
ctx = TraceContextManager.getLocalContext()
|
|
208
|
+
if not ctx or ctx.active_httpc_hash:
|
|
209
|
+
try:
|
|
210
|
+
return await fn(*args, **kwargs)
|
|
211
|
+
except Exception as e:
|
|
212
|
+
interceptor_step_error(e, ctx=ctx)
|
|
213
|
+
raise
|
|
214
|
+
|
|
215
|
+
param = None
|
|
216
|
+
method = None
|
|
217
|
+
if httpc_url.find('?') > -1:
|
|
218
|
+
httpc_url, param = httpc_url.split('?')
|
|
219
|
+
|
|
220
|
+
is_llm = _detect_llm_api(ctx, httpc_url)
|
|
221
|
+
if not ctx.step_id:
|
|
222
|
+
seq = getattr(ctx, '_httpc_seq', 1)
|
|
223
|
+
ctx.step_id = seq
|
|
224
|
+
ctx._httpc_seq = seq + 1
|
|
225
|
+
|
|
226
|
+
start_time = DateUtil.nowSystem()
|
|
227
|
+
ctx.start_time = start_time
|
|
228
|
+
ctx.httpc_url = httpc_url
|
|
229
|
+
ctx.active_httpc_hash = ctx.httpc_url
|
|
230
|
+
ctx.active_httpc_start_time = DateUtil.nowSystem()
|
|
231
|
+
|
|
232
|
+
callback = None
|
|
233
|
+
try:
|
|
234
|
+
callback = await fn(*args, **kwargs)
|
|
235
|
+
return callback
|
|
236
|
+
except Exception as e:
|
|
237
|
+
interceptor_step_error(e, ctx=ctx)
|
|
238
|
+
raise
|
|
239
|
+
finally:
|
|
240
|
+
try:
|
|
241
|
+
if ctx.driver == 'LLM API' and callback is not None:
|
|
242
|
+
status_code = getattr(callback, 'status_code', None)
|
|
243
|
+
if status_code:
|
|
244
|
+
_update_api_status_stats(ctx, status_code)
|
|
245
|
+
if status_code >= 400:
|
|
246
|
+
try:
|
|
247
|
+
aread_fn = getattr(callback, 'aread', None)
|
|
248
|
+
if callable(aread_fn):
|
|
249
|
+
try:
|
|
250
|
+
await aread_fn()
|
|
251
|
+
except Exception:
|
|
252
|
+
pass
|
|
253
|
+
else:
|
|
254
|
+
read_fn = getattr(callback, 'read', None)
|
|
255
|
+
if callable(read_fn):
|
|
256
|
+
try:
|
|
257
|
+
read_fn()
|
|
258
|
+
except Exception:
|
|
259
|
+
pass
|
|
260
|
+
error_text = callback.text or ''
|
|
261
|
+
except Exception as e:
|
|
262
|
+
logging.warning('[HTTPC] LLM API error text read failed: %s' % e, extra={'id': 'HTTPC001'})
|
|
263
|
+
error_text = ''
|
|
264
|
+
try:
|
|
265
|
+
interceptor_step_error(
|
|
266
|
+
Exception('[LLM API Error] HTTP {} {}'.format(status_code, error_text)),
|
|
267
|
+
ctx=ctx
|
|
268
|
+
)
|
|
269
|
+
except Exception as e:
|
|
270
|
+
logging.warning('[HTTPC] interceptor_step_error failed: %s' % e, extra={'id': 'HTTPC002'})
|
|
271
|
+
except Exception as e:
|
|
272
|
+
logging.warning('[HTTPC] LLM API error handling failed: %s' % e, extra={'id': 'HTTPC003'})
|
|
273
|
+
|
|
274
|
+
datas = [ctx.httpc_url, ctx.step_id, ctx.driver]
|
|
275
|
+
ctx.elapsed = DateUtil.nowSystem() - start_time
|
|
276
|
+
async_sender.send_packet(PacketTypeEnum.TX_HTTPC, ctx, datas)
|
|
277
|
+
|
|
278
|
+
if conf.profile_http_parameter_enabled and len(args) > 1:
|
|
279
|
+
try:
|
|
280
|
+
if type(args[1]) == dict:
|
|
281
|
+
param = (args[1].body if 'body' in args[1] else args[1]) or param
|
|
282
|
+
method = args[1].method if 'method' in args[1] else args[1]
|
|
283
|
+
|
|
284
|
+
if param:
|
|
285
|
+
datas = ['HTTP-PARAMETERS', method, param]
|
|
286
|
+
ctx.start_time = DateUtil.nowSystem()
|
|
287
|
+
async_sender.send_packet(PacketTypeEnum.TX_SECURE_MSG, ctx, datas)
|
|
288
|
+
except Exception as _e:
|
|
289
|
+
# finally 가 어떤 일이 있어도 return 값을 exception 으로 둔갑시키지 않도록 방어
|
|
290
|
+
logging.warning(
|
|
291
|
+
'[HTTPC] profile_http_parameter_enabled extract failed: %s' % _e,
|
|
292
|
+
extra={'id': 'HTTPC004'}
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
if is_llm:
|
|
296
|
+
ctx._llm_step_id = ctx.step_id
|
|
297
|
+
ctx.active_httpc_hash = 0
|
|
298
|
+
ctx.active_httpc_start_time = 0
|
|
299
|
+
ctx.httpc_url = None
|
|
300
|
+
ctx.step_id = 0
|
|
301
|
+
ctx.driver = ''
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def interceptor_sock_connect(fn, *args, **kwargs):
|
|
305
|
+
ctx = TraceContextManager.getLocalContext()
|
|
306
|
+
if not ctx:
|
|
307
|
+
return fn(*args, **kwargs)
|
|
308
|
+
|
|
309
|
+
try:
|
|
310
|
+
ctx.socket_connecting = True
|
|
311
|
+
callback = fn(*args, **kwargs)
|
|
312
|
+
return callback
|
|
313
|
+
except Exception as e:
|
|
314
|
+
interceptor_step_error(e)
|
|
315
|
+
finally:
|
|
316
|
+
ctx.socket_connecting = False
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def inter_tx_trace_auto_on(ctx):
|
|
320
|
+
try:
|
|
321
|
+
if isinstance(conf.mtrace_rate, str):
|
|
322
|
+
conf.mtrace_rate = int(conf.mtrace_rate)
|
|
323
|
+
except ValueError:
|
|
324
|
+
conf.mtrace_rate = 0
|
|
325
|
+
finally:
|
|
326
|
+
if conf.mtrace_rate <= 0 or ctx.httpc_checked or ctx.mtid != 0:
|
|
327
|
+
return
|
|
328
|
+
|
|
329
|
+
ctx.httpc_checked = True
|
|
330
|
+
|
|
331
|
+
try:
|
|
332
|
+
inter_tx_trace_auto_on.check_seq += 1
|
|
333
|
+
except AttributeError:
|
|
334
|
+
inter_tx_trace_auto_on.check_seq = 1
|
|
335
|
+
finally:
|
|
336
|
+
check_seq = inter_tx_trace_auto_on.check_seq
|
|
337
|
+
|
|
338
|
+
rate = int(conf.mtrace_rate / 10)
|
|
339
|
+
if rate == 10:
|
|
340
|
+
ctx.mtid = TraceContextManager.getId()
|
|
341
|
+
elif rate == 9:
|
|
342
|
+
if check_seq % 10 != 0:
|
|
343
|
+
ctx.mtid = TraceContextManager.getId()
|
|
344
|
+
elif rate == 8:
|
|
345
|
+
if check_seq % 5 != 0:
|
|
346
|
+
ctx.mtid = TraceContextManager.getId()
|
|
347
|
+
elif rate == 7:
|
|
348
|
+
if check_seq % 4 != 0:
|
|
349
|
+
ctx.mtid = TraceContextManager.getId()
|
|
350
|
+
elif rate == 6:
|
|
351
|
+
if check_seq % 3 != 0:
|
|
352
|
+
ctx.mtid = TraceContextManager.getId()
|
|
353
|
+
elif rate == 5:
|
|
354
|
+
if check_seq % 2 == 0:
|
|
355
|
+
ctx.mtid = TraceContextManager.getId()
|
|
356
|
+
elif rate == 4:
|
|
357
|
+
if check_seq % 3 == 0 or check_seq % 5 == 0:
|
|
358
|
+
ctx.mtid = TraceContextManager.getId()
|
|
359
|
+
elif rate == 3:
|
|
360
|
+
if check_seq % 4 == 0 or check_seq % 5 == 0:
|
|
361
|
+
ctx.mtid = TraceContextManager.getId()
|
|
362
|
+
elif rate == 2:
|
|
363
|
+
if check_seq % 5 == 0:
|
|
364
|
+
ctx.mtid = TraceContextManager.getId()
|
|
365
|
+
elif rate == 1:
|
|
366
|
+
if check_seq % 10 == 0:
|
|
367
|
+
ctx.mtid = TraceContextManager.getId()
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def transfer(headers):
|
|
371
|
+
ctx = TraceContextManager.getLocalContext()
|
|
372
|
+
|
|
373
|
+
if not ctx.mtid:
|
|
374
|
+
inter_tx_trace_auto_on(ctx)
|
|
375
|
+
|
|
376
|
+
if ctx.mtid:
|
|
377
|
+
if not ctx.step_id:
|
|
378
|
+
seq = getattr(ctx, '_httpc_seq', 1)
|
|
379
|
+
ctx.step_id = seq
|
|
380
|
+
ctx._httpc_seq = seq + 1
|
|
381
|
+
headers[conf._trace_mtrace_caller_key] = ctx.transfer()
|
|
382
|
+
if conf.stat_mtrace_enabled:
|
|
383
|
+
headers[conf._trace_mtrace_info_key] = ctx.transferInfo()
|
|
384
|
+
|
|
385
|
+
ctx.mcallee = TraceContextManager.getId()
|
|
386
|
+
headers[conf._trace_mtrace_callee_key] = Hexa32.toString32(ctx.mcallee)
|
|
387
|
+
headers[conf._trace_mtrace_caller_poid_key] = ctx.transferPOID()
|
|
388
|
+
return headers
|