whatap-python 2.1.0__py3-none-any.whl → 2.1.1__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/agent/darwin/amd64/whatap_python +0 -0
- whatap/agent/darwin/arm64/whatap_python +0 -0
- whatap/build.py +2 -2
- whatap/llm/providers/anthropic/messages/messages.py +48 -32
- whatap/llm/providers/anthropic/messages/messages_extractor.py +5 -1
- whatap/llm/providers/interceptor.py +55 -5
- whatap/llm/providers/openai/chat/chat.py +63 -42
- whatap/llm/providers/openai/chat/chat_extractor.py +2 -0
- whatap/llm/providers/openai/completions/completions.py +48 -32
- whatap/llm/providers/openai/embeddings/embeddings.py +41 -25
- whatap/llm/providers/openai/responses/responses.py +54 -32
- whatap/llm/providers/openai/responses/responses_extractor.py +3 -0
- whatap/llm/providers/stream_accumulator.py +71 -5
- {whatap_python-2.1.0.dist-info → whatap_python-2.1.1.dist-info}/METADATA +1 -1
- {whatap_python-2.1.0.dist-info → whatap_python-2.1.1.dist-info}/RECORD +18 -18
- {whatap_python-2.1.0.dist-info → whatap_python-2.1.1.dist-info}/WHEEL +0 -0
- {whatap_python-2.1.0.dist-info → whatap_python-2.1.1.dist-info}/entry_points.txt +0 -0
- {whatap_python-2.1.0.dist-info → whatap_python-2.1.1.dist-info}/top_level.txt +0 -0
|
Binary file
|
|
Binary file
|
whatap/build.py
CHANGED
|
@@ -3,68 +3,84 @@ from anthropic import APIError
|
|
|
3
3
|
|
|
4
4
|
from whatap.llm.providers.interceptor import (
|
|
5
5
|
before_call, handle_error, after_call, finalize_non_streaming, _ensure_end,
|
|
6
|
-
capture_client,
|
|
6
|
+
capture_client, extract_response, _safe, _clear_httpc_pending,
|
|
7
7
|
)
|
|
8
|
-
from whatap.llm.providers.stream_accumulator import
|
|
8
|
+
from whatap.llm.providers.stream_accumulator import wrap_sync_stream, wrap_async_stream
|
|
9
9
|
from whatap.llm.providers.anthropic.messages.messages_context import build_context
|
|
10
10
|
from whatap.llm.providers.anthropic.messages.messages_extractor import finalize, AnthropicStream
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def intercept_create(fn, *args, **kwargs):
|
|
14
|
-
"""Anthropic Messages 동기 호출을
|
|
15
|
-
pack
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
"""Anthropic Messages 동기 호출을 인터셉트한다. 계측 실패는 사용자 호출로 전파되지 않는다."""
|
|
15
|
+
pack = active_key = None
|
|
16
|
+
try:
|
|
17
|
+
pack, ctx, features, stream = build_context(kwargs)
|
|
18
|
+
capture_client(pack, ctx, args)
|
|
19
|
+
active_key = (pack.model, pack.operation_type, getattr(pack, "prompt_version", "v1"))
|
|
20
|
+
before_call(pack, active_key)
|
|
21
|
+
except Exception:
|
|
22
|
+
if pack is not None and active_key is not None:
|
|
23
|
+
_safe(_ensure_end, pack, active_key)
|
|
24
|
+
return fn(*args, **kwargs)
|
|
18
25
|
|
|
19
|
-
before_call(pack, active_key)
|
|
20
26
|
_stream_returned = False
|
|
21
27
|
try:
|
|
22
28
|
try:
|
|
23
29
|
response = fn(*args, **kwargs)
|
|
24
30
|
except Exception as err:
|
|
25
|
-
handle_error
|
|
31
|
+
_safe(handle_error, pack, err, active_key, APIError)
|
|
26
32
|
raise
|
|
27
33
|
finally:
|
|
28
|
-
|
|
29
|
-
ctx._llm_httpc_pending = False
|
|
34
|
+
_safe(_clear_httpc_pending, ctx)
|
|
30
35
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
try:
|
|
37
|
+
after_call(pack, ctx)
|
|
38
|
+
if stream:
|
|
39
|
+
result, _stream_returned = wrap_sync_stream(response, AnthropicStream(pack, active_key, features))
|
|
40
|
+
return result
|
|
41
|
+
extract_response(response, finalize, pack, features)
|
|
42
|
+
finalize_non_streaming(pack, active_key)
|
|
43
|
+
except Exception:
|
|
44
|
+
pass
|
|
37
45
|
return response
|
|
38
46
|
finally:
|
|
39
47
|
if not _stream_returned:
|
|
40
|
-
_ensure_end
|
|
48
|
+
_safe(_ensure_end, pack, active_key)
|
|
41
49
|
|
|
42
50
|
|
|
43
51
|
async def async_intercept_create(fn, *args, **kwargs):
|
|
44
|
-
"""Anthropic Messages 비동기 호출을
|
|
45
|
-
pack
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
"""Anthropic Messages 비동기 호출을 인터셉트한다. 계측 실패는 사용자 호출로 전파되지 않는다."""
|
|
53
|
+
pack = active_key = None
|
|
54
|
+
try:
|
|
55
|
+
pack, ctx, features, stream = build_context(kwargs)
|
|
56
|
+
capture_client(pack, ctx, args)
|
|
57
|
+
active_key = (pack.model, pack.operation_type, getattr(pack, "prompt_version", "v1"))
|
|
58
|
+
before_call(pack, active_key)
|
|
59
|
+
except Exception:
|
|
60
|
+
if pack is not None and active_key is not None:
|
|
61
|
+
_safe(_ensure_end, pack, active_key)
|
|
62
|
+
return await fn(*args, **kwargs)
|
|
48
63
|
|
|
49
|
-
before_call(pack, active_key)
|
|
50
64
|
_stream_returned = False
|
|
51
65
|
try:
|
|
52
66
|
try:
|
|
53
67
|
response = await fn(*args, **kwargs)
|
|
54
68
|
except Exception as err:
|
|
55
|
-
handle_error
|
|
69
|
+
_safe(handle_error, pack, err, active_key, APIError)
|
|
56
70
|
raise
|
|
57
71
|
finally:
|
|
58
|
-
|
|
59
|
-
ctx._llm_httpc_pending = False
|
|
72
|
+
_safe(_clear_httpc_pending, ctx)
|
|
60
73
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
74
|
+
try:
|
|
75
|
+
after_call(pack, ctx)
|
|
76
|
+
if stream:
|
|
77
|
+
result, _stream_returned = wrap_async_stream(response, AnthropicStream(pack, active_key, features))
|
|
78
|
+
return result
|
|
79
|
+
extract_response(response, finalize, pack, features)
|
|
80
|
+
finalize_non_streaming(pack, active_key)
|
|
81
|
+
except Exception:
|
|
82
|
+
pass
|
|
67
83
|
return response
|
|
68
84
|
finally:
|
|
69
85
|
if not _stream_returned:
|
|
70
|
-
_ensure_end
|
|
86
|
+
_safe(_ensure_end, pack, active_key)
|
|
@@ -84,6 +84,7 @@ class AnthropicStream(StreamAccumulator):
|
|
|
84
84
|
self.block_type = getattr(block, 'type', None)
|
|
85
85
|
self.block_name = getattr(block, 'name', None)
|
|
86
86
|
if self.block_type == 'tool_use':
|
|
87
|
+
self.on_first_token()
|
|
87
88
|
tag = (LlmFeature.COMPUTER_USE
|
|
88
89
|
if self.block_name and 'computer' in self.block_name
|
|
89
90
|
else LlmFeature.TOOL_USE)
|
|
@@ -92,7 +93,10 @@ class AnthropicStream(StreamAccumulator):
|
|
|
92
93
|
elif t == 'content_block_delta':
|
|
93
94
|
delta = getattr(event, 'delta', None)
|
|
94
95
|
if self.block_type == 'thinking':
|
|
95
|
-
|
|
96
|
+
thinking = getattr(delta, 'thinking', '') or ''
|
|
97
|
+
if thinking:
|
|
98
|
+
self.on_first_token()
|
|
99
|
+
self.reasoning += thinking
|
|
96
100
|
elif self.block_type == 'text':
|
|
97
101
|
text = getattr(delta, 'text', '') or ''
|
|
98
102
|
if text:
|
|
@@ -6,6 +6,7 @@ API 호출 전후 처리 흐름:
|
|
|
6
6
|
"""
|
|
7
7
|
import time
|
|
8
8
|
|
|
9
|
+
from whatap import logging
|
|
9
10
|
from whatap.counter.tasks.llm_log_sink_task import dispatch_llm_pack
|
|
10
11
|
|
|
11
12
|
|
|
@@ -93,6 +94,25 @@ def _active_stat():
|
|
|
93
94
|
return LlmStatTask.get_stat('ActiveStat')
|
|
94
95
|
|
|
95
96
|
|
|
97
|
+
def _safe(fn, *args, **kwargs):
|
|
98
|
+
"""계측 보조 단계를 안전하게 실행한다 — 예외를 흡수(디버그 로깅)해 사용자 호출을 보호.
|
|
99
|
+
|
|
100
|
+
계측은 어떤 경우에도 사용자 애플리케이션을 깨면 안 된다. 인터셉트 라이프사이클의
|
|
101
|
+
모든 보조 호출(before/after/handle_error/_ensure_end 등)을 이걸로 감싼다.
|
|
102
|
+
"""
|
|
103
|
+
try:
|
|
104
|
+
return fn(*args, **kwargs)
|
|
105
|
+
except Exception as e:
|
|
106
|
+
logging.debug('[LLM] instrumentation step skipped: %s' % e, extra={'id': 'LLM008'})
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _clear_httpc_pending(ctx):
|
|
111
|
+
"""fn() 직후 httpc pending 플래그 해제(있을 때만)."""
|
|
112
|
+
if ctx is not None:
|
|
113
|
+
ctx._llm_httpc_pending = False
|
|
114
|
+
|
|
115
|
+
|
|
96
116
|
def before_call(pack, active_key):
|
|
97
117
|
"""API 호출 전: active 카운터 증가 + 시작 시간 기록 + 순차 인덱스 할당."""
|
|
98
118
|
pack._active_ended = False
|
|
@@ -174,9 +194,39 @@ def finalize_non_streaming(pack, active_key):
|
|
|
174
194
|
_ensure_end(pack, active_key)
|
|
175
195
|
|
|
176
196
|
|
|
197
|
+
def extract_response(response, finalize_fn, pack, *finalize_args):
|
|
198
|
+
"""비스트리밍 응답을 계측한다. 어떤 예외도 사용자 호출로 전파시키지 않는다.
|
|
199
|
+
|
|
200
|
+
계측은 사용자 애플리케이션을 절대 깨면 안 된다. finalize_fn 은 응답 구조
|
|
201
|
+
(``.choices`` / ``.content`` / ``.output`` 등) 를 단정하므로, 예상 밖 응답
|
|
202
|
+
(예: litellm/langchain 이 ``with_raw_response`` 로 받는 ``LegacyAPIResponse``)
|
|
203
|
+
이 와도 여기서 흡수하고 계측만 생략한다.
|
|
204
|
+
|
|
205
|
+
또한 응답이 ``parse()`` 를 가진 raw 래퍼면 parse() 로 실제 응답을 꺼내 계측한다.
|
|
206
|
+
parse() 결과는 캐시되어 호출측(litellm 등)의 후속 parse() 와 공유된다.
|
|
207
|
+
"""
|
|
208
|
+
try:
|
|
209
|
+
target = response
|
|
210
|
+
parse = getattr(response, "parse", None)
|
|
211
|
+
if callable(parse):
|
|
212
|
+
try:
|
|
213
|
+
target = parse()
|
|
214
|
+
except Exception:
|
|
215
|
+
target = response
|
|
216
|
+
finalize_fn(target, pack, *finalize_args)
|
|
217
|
+
except Exception as e:
|
|
218
|
+
logging.debug('[LLM] response extract skipped: %s' % e, extra={'id': 'LLM005'})
|
|
219
|
+
|
|
220
|
+
|
|
177
221
|
def _dispatch(pack):
|
|
178
|
-
"""로그싱크팩 전송 + 메트릭 stat 업데이트 통합 호출."""
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
222
|
+
"""로그싱크팩 전송 + 메트릭 stat 업데이트 통합 호출. 송출 실패는 사용자에게 전파 안 함."""
|
|
223
|
+
try:
|
|
224
|
+
dispatch_llm_pack(pack)
|
|
225
|
+
except Exception as e:
|
|
226
|
+
logging.debug('[LLM] dispatch failed: %s' % e, extra={'id': 'LLM006'})
|
|
227
|
+
try:
|
|
228
|
+
inst = _stat_task()
|
|
229
|
+
if inst:
|
|
230
|
+
inst.notify(pack)
|
|
231
|
+
except Exception as e:
|
|
232
|
+
logging.debug('[LLM] stat notify failed: %s' % e, extra={'id': 'LLM007'})
|
|
@@ -3,80 +3,101 @@ from openai import OpenAIError
|
|
|
3
3
|
|
|
4
4
|
from whatap.llm.providers.interceptor import (
|
|
5
5
|
before_call, handle_error, after_call, finalize_non_streaming, _ensure_end,
|
|
6
|
-
capture_client,
|
|
6
|
+
capture_client, extract_response, _safe, _clear_httpc_pending,
|
|
7
7
|
)
|
|
8
|
-
from whatap.llm.providers.stream_accumulator import
|
|
8
|
+
from whatap.llm.providers.stream_accumulator import wrap_sync_stream, wrap_async_stream
|
|
9
9
|
from whatap.llm.providers.openai.chat.chat_context import build_context
|
|
10
10
|
from whatap.llm.providers.openai.chat.chat_extractor import finalize, ChatStream
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def intercept_create(fn, *args, **kwargs):
|
|
14
|
-
"""OpenAI Chat Completions 동기 호출을 인터셉트하여 모니터링 데이터를 수집한다.
|
|
15
|
-
pack, ctx, features, stream = build_context(kwargs)
|
|
16
|
-
capture_client(pack, ctx, args)
|
|
17
|
-
active_key = (pack.model, pack.operation_type, getattr(pack, "prompt_version", "v1"))
|
|
14
|
+
"""OpenAI Chat Completions 동기 호출을 인터셉트하여 모니터링 데이터를 수집한다.
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
계측 어느 단계가 실패해도 사용자 호출은 보호된다 — 계측 예외는 전파하지 않고
|
|
17
|
+
사용자 fn 의 예외만 전파한다.
|
|
18
|
+
"""
|
|
19
|
+
pack = active_key = None
|
|
20
|
+
try:
|
|
21
|
+
pack, ctx, features, stream = build_context(kwargs)
|
|
22
|
+
capture_client(pack, ctx, args)
|
|
23
|
+
active_key = (pack.model, pack.operation_type, getattr(pack, "prompt_version", "v1"))
|
|
24
|
+
if stream:
|
|
25
|
+
opts = dict(kwargs.get("stream_options") or {})
|
|
26
|
+
if not opts.get("include_usage"):
|
|
27
|
+
opts["include_usage"] = True
|
|
28
|
+
kwargs["stream_options"] = opts
|
|
29
|
+
before_call(pack, active_key)
|
|
30
|
+
except Exception:
|
|
31
|
+
if pack is not None and active_key is not None:
|
|
32
|
+
_safe(_ensure_end, pack, active_key)
|
|
33
|
+
return fn(*args, **kwargs)
|
|
24
34
|
|
|
25
|
-
before_call(pack, active_key)
|
|
26
35
|
_stream_returned = False
|
|
27
36
|
try:
|
|
28
37
|
try:
|
|
29
38
|
response = fn(*args, **kwargs)
|
|
30
39
|
except Exception as err:
|
|
31
|
-
handle_error
|
|
40
|
+
_safe(handle_error, pack, err, active_key, OpenAIError)
|
|
32
41
|
raise
|
|
33
42
|
finally:
|
|
34
|
-
|
|
35
|
-
ctx._llm_httpc_pending = False
|
|
43
|
+
_safe(_clear_httpc_pending, ctx)
|
|
36
44
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
try:
|
|
46
|
+
after_call(pack, ctx)
|
|
47
|
+
if stream:
|
|
48
|
+
result, _stream_returned = wrap_sync_stream(response, ChatStream(pack, active_key))
|
|
49
|
+
return result
|
|
50
|
+
extract_response(response, finalize, pack, features)
|
|
51
|
+
finalize_non_streaming(pack, active_key)
|
|
52
|
+
except Exception:
|
|
53
|
+
pass
|
|
43
54
|
return response
|
|
44
55
|
finally:
|
|
45
56
|
if not _stream_returned:
|
|
46
|
-
_ensure_end
|
|
57
|
+
_safe(_ensure_end, pack, active_key)
|
|
47
58
|
|
|
48
59
|
|
|
49
60
|
async def intercept_create_async(fn, *args, **kwargs):
|
|
50
|
-
"""OpenAI Chat Completions 비동기 호출을 인터셉트하여 모니터링 데이터를 수집한다.
|
|
51
|
-
pack, ctx, features, stream = build_context(kwargs)
|
|
52
|
-
capture_client(pack, ctx, args)
|
|
53
|
-
active_key = (pack.model, pack.operation_type, getattr(pack, "prompt_version", "v1"))
|
|
61
|
+
"""OpenAI Chat Completions 비동기 호출을 인터셉트하여 모니터링 데이터를 수집한다.
|
|
54
62
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
계측 어느 단계가 실패해도 사용자 호출은 보호된다.
|
|
64
|
+
"""
|
|
65
|
+
pack = active_key = None
|
|
66
|
+
try:
|
|
67
|
+
pack, ctx, features, stream = build_context(kwargs)
|
|
68
|
+
capture_client(pack, ctx, args)
|
|
69
|
+
active_key = (pack.model, pack.operation_type, getattr(pack, "prompt_version", "v1"))
|
|
70
|
+
if stream:
|
|
71
|
+
opts = dict(kwargs.get("stream_options") or {})
|
|
72
|
+
if not opts.get("include_usage"):
|
|
73
|
+
opts["include_usage"] = True
|
|
74
|
+
kwargs["stream_options"] = opts
|
|
75
|
+
before_call(pack, active_key)
|
|
76
|
+
except Exception:
|
|
77
|
+
if pack is not None and active_key is not None:
|
|
78
|
+
_safe(_ensure_end, pack, active_key)
|
|
79
|
+
return await fn(*args, **kwargs)
|
|
60
80
|
|
|
61
|
-
before_call(pack, active_key)
|
|
62
81
|
_stream_returned = False
|
|
63
82
|
try:
|
|
64
83
|
try:
|
|
65
84
|
response = await fn(*args, **kwargs)
|
|
66
85
|
except Exception as err:
|
|
67
|
-
handle_error
|
|
86
|
+
_safe(handle_error, pack, err, active_key, OpenAIError)
|
|
68
87
|
raise
|
|
69
88
|
finally:
|
|
70
|
-
|
|
71
|
-
ctx._llm_httpc_pending = False
|
|
89
|
+
_safe(_clear_httpc_pending, ctx)
|
|
72
90
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
91
|
+
try:
|
|
92
|
+
after_call(pack, ctx)
|
|
93
|
+
if stream:
|
|
94
|
+
result, _stream_returned = wrap_async_stream(response, ChatStream(pack, active_key))
|
|
95
|
+
return result
|
|
96
|
+
extract_response(response, finalize, pack, features)
|
|
97
|
+
finalize_non_streaming(pack, active_key)
|
|
98
|
+
except Exception:
|
|
99
|
+
pass
|
|
79
100
|
return response
|
|
80
101
|
finally:
|
|
81
102
|
if not _stream_returned:
|
|
82
|
-
_ensure_end
|
|
103
|
+
_safe(_ensure_end, pack, active_key)
|
|
@@ -89,9 +89,11 @@ class ChatStream(StreamAccumulator):
|
|
|
89
89
|
getattr(delta, "reasoning_content", None) or
|
|
90
90
|
getattr(delta, "reasoning", None) or "")
|
|
91
91
|
if reasoning:
|
|
92
|
+
self.on_first_token()
|
|
92
93
|
self.reasoning += reasoning
|
|
93
94
|
|
|
94
95
|
if getattr(delta, "tool_calls", None):
|
|
96
|
+
self.on_first_token()
|
|
95
97
|
self.has_tool = True
|
|
96
98
|
for tc in delta.tool_calls:
|
|
97
99
|
idx = tc.index
|
|
@@ -3,68 +3,84 @@ from openai import OpenAIError
|
|
|
3
3
|
|
|
4
4
|
from whatap.llm.providers.interceptor import (
|
|
5
5
|
before_call, handle_error, after_call, finalize_non_streaming, _ensure_end,
|
|
6
|
-
capture_client,
|
|
6
|
+
capture_client, extract_response, _safe, _clear_httpc_pending,
|
|
7
7
|
)
|
|
8
|
-
from whatap.llm.providers.stream_accumulator import
|
|
8
|
+
from whatap.llm.providers.stream_accumulator import wrap_sync_stream, wrap_async_stream
|
|
9
9
|
from whatap.llm.providers.openai.completions.completions_context import build_context
|
|
10
10
|
from whatap.llm.providers.openai.completions.completions_extractor import finalize, CompletionsStream
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def intercept_completions(fn, *args, **kwargs):
|
|
14
|
-
"""Completions API 동기 인터셉트."""
|
|
15
|
-
pack
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
"""Completions API 동기 인터셉트. 계측 실패는 사용자 호출로 전파되지 않는다."""
|
|
15
|
+
pack = active_key = None
|
|
16
|
+
try:
|
|
17
|
+
pack, ctx = build_context(kwargs)
|
|
18
|
+
capture_client(pack, ctx, args)
|
|
19
|
+
active_key = (pack.model, pack.operation_type, getattr(pack, "prompt_version", "v1"))
|
|
20
|
+
before_call(pack, active_key)
|
|
21
|
+
except Exception:
|
|
22
|
+
if pack is not None and active_key is not None:
|
|
23
|
+
_safe(_ensure_end, pack, active_key)
|
|
24
|
+
return fn(*args, **kwargs)
|
|
18
25
|
|
|
19
|
-
before_call(pack, active_key)
|
|
20
26
|
_stream_returned = False
|
|
21
27
|
try:
|
|
22
28
|
try:
|
|
23
29
|
response = fn(*args, **kwargs)
|
|
24
30
|
except Exception as err:
|
|
25
|
-
handle_error
|
|
31
|
+
_safe(handle_error, pack, err, active_key, OpenAIError)
|
|
26
32
|
raise
|
|
27
33
|
finally:
|
|
28
|
-
|
|
29
|
-
ctx._llm_httpc_pending = False
|
|
34
|
+
_safe(_clear_httpc_pending, ctx)
|
|
30
35
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
try:
|
|
37
|
+
after_call(pack, ctx)
|
|
38
|
+
if pack.stream:
|
|
39
|
+
result, _stream_returned = wrap_sync_stream(response, CompletionsStream(pack, active_key))
|
|
40
|
+
return result
|
|
41
|
+
extract_response(response, finalize, pack)
|
|
42
|
+
finalize_non_streaming(pack, active_key)
|
|
43
|
+
except Exception:
|
|
44
|
+
pass
|
|
37
45
|
return response
|
|
38
46
|
finally:
|
|
39
47
|
if not _stream_returned:
|
|
40
|
-
_ensure_end
|
|
48
|
+
_safe(_ensure_end, pack, active_key)
|
|
41
49
|
|
|
42
50
|
|
|
43
51
|
async def intercept_completions_async(fn, *args, **kwargs):
|
|
44
|
-
"""Completions API 비동기 인터셉트."""
|
|
45
|
-
pack
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
"""Completions API 비동기 인터셉트. 계측 실패는 사용자 호출로 전파되지 않는다."""
|
|
53
|
+
pack = active_key = None
|
|
54
|
+
try:
|
|
55
|
+
pack, ctx = build_context(kwargs)
|
|
56
|
+
capture_client(pack, ctx, args)
|
|
57
|
+
active_key = (pack.model, pack.operation_type, getattr(pack, "prompt_version", "v1"))
|
|
58
|
+
before_call(pack, active_key)
|
|
59
|
+
except Exception:
|
|
60
|
+
if pack is not None and active_key is not None:
|
|
61
|
+
_safe(_ensure_end, pack, active_key)
|
|
62
|
+
return await fn(*args, **kwargs)
|
|
48
63
|
|
|
49
|
-
before_call(pack, active_key)
|
|
50
64
|
_stream_returned = False
|
|
51
65
|
try:
|
|
52
66
|
try:
|
|
53
67
|
response = await fn(*args, **kwargs)
|
|
54
68
|
except Exception as err:
|
|
55
|
-
handle_error
|
|
69
|
+
_safe(handle_error, pack, err, active_key, OpenAIError)
|
|
56
70
|
raise
|
|
57
71
|
finally:
|
|
58
|
-
|
|
59
|
-
ctx._llm_httpc_pending = False
|
|
72
|
+
_safe(_clear_httpc_pending, ctx)
|
|
60
73
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
74
|
+
try:
|
|
75
|
+
after_call(pack, ctx)
|
|
76
|
+
if pack.stream:
|
|
77
|
+
result, _stream_returned = wrap_async_stream(response, CompletionsStream(pack, active_key))
|
|
78
|
+
return result
|
|
79
|
+
extract_response(response, finalize, pack)
|
|
80
|
+
finalize_non_streaming(pack, active_key)
|
|
81
|
+
except Exception:
|
|
82
|
+
pass
|
|
67
83
|
return response
|
|
68
84
|
finally:
|
|
69
85
|
if not _stream_returned:
|
|
70
|
-
_ensure_end
|
|
86
|
+
_safe(_ensure_end, pack, active_key)
|
|
@@ -3,57 +3,73 @@ from openai import OpenAIError
|
|
|
3
3
|
|
|
4
4
|
from whatap.llm.providers.interceptor import (
|
|
5
5
|
before_call, handle_error, after_call, finalize_non_streaming, _ensure_end,
|
|
6
|
-
capture_client,
|
|
6
|
+
capture_client, extract_response, _safe, _clear_httpc_pending,
|
|
7
7
|
)
|
|
8
8
|
from whatap.llm.providers.openai.embeddings.embeddings_context import build_context
|
|
9
9
|
from whatap.llm.providers.openai.embeddings.embeddings_extractor import finalize
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def intercept_embeddings(fn, *args, **kwargs):
|
|
13
|
-
"""OpenAI Embeddings 동기 호출을
|
|
14
|
-
pack
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
"""OpenAI Embeddings 동기 호출을 인터셉트한다. 계측 실패는 사용자 호출로 전파되지 않는다."""
|
|
14
|
+
pack = active_key = None
|
|
15
|
+
try:
|
|
16
|
+
pack, ctx = build_context(kwargs)
|
|
17
|
+
capture_client(pack, ctx, args)
|
|
18
|
+
active_key = (pack.model, pack.operation_type, getattr(pack, "prompt_version", "v1"))
|
|
19
|
+
before_call(pack, active_key)
|
|
20
|
+
except Exception:
|
|
21
|
+
if pack is not None and active_key is not None:
|
|
22
|
+
_safe(_ensure_end, pack, active_key)
|
|
23
|
+
return fn(*args, **kwargs)
|
|
17
24
|
|
|
18
|
-
before_call(pack, active_key)
|
|
19
25
|
try:
|
|
20
26
|
try:
|
|
21
27
|
response = fn(*args, **kwargs)
|
|
22
28
|
except Exception as err:
|
|
23
|
-
handle_error
|
|
29
|
+
_safe(handle_error, pack, err, active_key, OpenAIError)
|
|
24
30
|
raise
|
|
25
31
|
finally:
|
|
26
|
-
|
|
27
|
-
ctx._llm_httpc_pending = False
|
|
32
|
+
_safe(_clear_httpc_pending, ctx)
|
|
28
33
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
try:
|
|
35
|
+
after_call(pack, ctx)
|
|
36
|
+
extract_response(response, finalize, pack, kwargs)
|
|
37
|
+
finalize_non_streaming(pack, active_key)
|
|
38
|
+
except Exception:
|
|
39
|
+
pass
|
|
32
40
|
return response
|
|
33
41
|
finally:
|
|
34
|
-
_ensure_end
|
|
42
|
+
_safe(_ensure_end, pack, active_key)
|
|
35
43
|
|
|
36
44
|
|
|
37
45
|
async def intercept_embeddings_async(fn, *args, **kwargs):
|
|
38
|
-
"""OpenAI Embeddings 비동기 호출을
|
|
39
|
-
pack
|
|
40
|
-
|
|
41
|
-
|
|
46
|
+
"""OpenAI Embeddings 비동기 호출을 인터셉트한다. 계측 실패는 사용자 호출로 전파되지 않는다."""
|
|
47
|
+
pack = active_key = None
|
|
48
|
+
try:
|
|
49
|
+
pack, ctx = build_context(kwargs)
|
|
50
|
+
capture_client(pack, ctx, args)
|
|
51
|
+
active_key = (pack.model, pack.operation_type, getattr(pack, "prompt_version", "v1"))
|
|
52
|
+
before_call(pack, active_key)
|
|
53
|
+
except Exception:
|
|
54
|
+
if pack is not None and active_key is not None:
|
|
55
|
+
_safe(_ensure_end, pack, active_key)
|
|
56
|
+
return await fn(*args, **kwargs)
|
|
42
57
|
|
|
43
|
-
before_call(pack, active_key)
|
|
44
58
|
try:
|
|
45
59
|
try:
|
|
46
60
|
response = await fn(*args, **kwargs)
|
|
47
61
|
except Exception as err:
|
|
48
|
-
handle_error
|
|
62
|
+
_safe(handle_error, pack, err, active_key, OpenAIError)
|
|
49
63
|
raise
|
|
50
64
|
finally:
|
|
51
|
-
|
|
52
|
-
ctx._llm_httpc_pending = False
|
|
65
|
+
_safe(_clear_httpc_pending, ctx)
|
|
53
66
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
67
|
+
try:
|
|
68
|
+
after_call(pack, ctx)
|
|
69
|
+
extract_response(response, finalize, pack, kwargs)
|
|
70
|
+
finalize_non_streaming(pack, active_key)
|
|
71
|
+
except Exception:
|
|
72
|
+
pass
|
|
57
73
|
return response
|
|
58
74
|
finally:
|
|
59
|
-
_ensure_end
|
|
75
|
+
_safe(_ensure_end, pack, active_key)
|
|
@@ -3,68 +3,90 @@ from openai import OpenAIError
|
|
|
3
3
|
|
|
4
4
|
from whatap.llm.providers.interceptor import (
|
|
5
5
|
before_call, handle_error, after_call, finalize_non_streaming, _ensure_end,
|
|
6
|
-
capture_client,
|
|
6
|
+
capture_client, extract_response, _safe, _clear_httpc_pending,
|
|
7
7
|
)
|
|
8
|
-
from whatap.llm.providers.stream_accumulator import
|
|
8
|
+
from whatap.llm.providers.stream_accumulator import wrap_sync_stream, wrap_async_stream
|
|
9
9
|
from whatap.llm.providers.openai.responses.responses_context import build_context
|
|
10
10
|
from whatap.llm.providers.openai.responses.responses_extractor import finalize, ResponsesStream
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def intercept_responses_create(fn, *args, **kwargs):
|
|
14
|
-
"""OpenAI Responses 동기 호출을 인터셉트하여 모니터링 데이터를 수집한다.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
"""OpenAI Responses 동기 호출을 인터셉트하여 모니터링 데이터를 수집한다.
|
|
15
|
+
|
|
16
|
+
계측 어느 단계가 실패해도 사용자 호출은 보호된다.
|
|
17
|
+
"""
|
|
18
|
+
pack = active_key = None
|
|
19
|
+
try:
|
|
20
|
+
pack, ctx, features, stream = build_context(kwargs)
|
|
21
|
+
capture_client(pack, ctx, args)
|
|
22
|
+
active_key = (pack.model, pack.operation_type, getattr(pack, "prompt_version", "v1"))
|
|
23
|
+
before_call(pack, active_key)
|
|
24
|
+
except Exception:
|
|
25
|
+
if pack is not None and active_key is not None:
|
|
26
|
+
_safe(_ensure_end, pack, active_key)
|
|
27
|
+
return fn(*args, **kwargs)
|
|
18
28
|
|
|
19
|
-
before_call(pack, active_key)
|
|
20
29
|
_stream_returned = False
|
|
21
30
|
try:
|
|
22
31
|
try:
|
|
23
32
|
response = fn(*args, **kwargs)
|
|
24
33
|
except Exception as err:
|
|
25
|
-
handle_error
|
|
34
|
+
_safe(handle_error, pack, err, active_key, OpenAIError)
|
|
26
35
|
raise
|
|
27
36
|
finally:
|
|
28
|
-
|
|
29
|
-
ctx._llm_httpc_pending = False
|
|
37
|
+
_safe(_clear_httpc_pending, ctx)
|
|
30
38
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
try:
|
|
40
|
+
after_call(pack, ctx)
|
|
41
|
+
if stream:
|
|
42
|
+
result, _stream_returned = wrap_sync_stream(response, ResponsesStream(pack, active_key))
|
|
43
|
+
return result
|
|
44
|
+
extract_response(response, finalize, pack, features)
|
|
45
|
+
finalize_non_streaming(pack, active_key)
|
|
46
|
+
except Exception:
|
|
47
|
+
pass
|
|
37
48
|
return response
|
|
38
49
|
finally:
|
|
39
50
|
if not _stream_returned:
|
|
40
|
-
_ensure_end
|
|
51
|
+
_safe(_ensure_end, pack, active_key)
|
|
41
52
|
|
|
42
53
|
|
|
43
54
|
async def intercept_responses_create_async(fn, *args, **kwargs):
|
|
44
|
-
"""OpenAI Responses 비동기 호출을 인터셉트하여 모니터링 데이터를 수집한다.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
55
|
+
"""OpenAI Responses 비동기 호출을 인터셉트하여 모니터링 데이터를 수집한다.
|
|
56
|
+
|
|
57
|
+
계측 어느 단계가 실패해도 사용자 호출은 보호된다.
|
|
58
|
+
"""
|
|
59
|
+
pack = active_key = None
|
|
60
|
+
try:
|
|
61
|
+
pack, ctx, features, stream = build_context(kwargs)
|
|
62
|
+
capture_client(pack, ctx, args)
|
|
63
|
+
active_key = (pack.model, pack.operation_type, getattr(pack, "prompt_version", "v1"))
|
|
64
|
+
before_call(pack, active_key)
|
|
65
|
+
except Exception:
|
|
66
|
+
if pack is not None and active_key is not None:
|
|
67
|
+
_safe(_ensure_end, pack, active_key)
|
|
68
|
+
return await fn(*args, **kwargs)
|
|
48
69
|
|
|
49
|
-
before_call(pack, active_key)
|
|
50
70
|
_stream_returned = False
|
|
51
71
|
try:
|
|
52
72
|
try:
|
|
53
73
|
response = await fn(*args, **kwargs)
|
|
54
74
|
except Exception as err:
|
|
55
|
-
handle_error
|
|
75
|
+
_safe(handle_error, pack, err, active_key, OpenAIError)
|
|
56
76
|
raise
|
|
57
77
|
finally:
|
|
58
|
-
|
|
59
|
-
ctx._llm_httpc_pending = False
|
|
78
|
+
_safe(_clear_httpc_pending, ctx)
|
|
60
79
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
80
|
+
try:
|
|
81
|
+
after_call(pack, ctx)
|
|
82
|
+
if stream:
|
|
83
|
+
result, _stream_returned = wrap_async_stream(response, ResponsesStream(pack, active_key))
|
|
84
|
+
return result
|
|
85
|
+
extract_response(response, finalize, pack, features)
|
|
86
|
+
finalize_non_streaming(pack, active_key)
|
|
87
|
+
except Exception:
|
|
88
|
+
pass
|
|
67
89
|
return response
|
|
68
90
|
finally:
|
|
69
91
|
if not _stream_returned:
|
|
70
|
-
_ensure_end
|
|
92
|
+
_safe(_ensure_end, pack, active_key)
|
|
@@ -92,15 +92,18 @@ class ResponsesStream(StreamAccumulator):
|
|
|
92
92
|
item = getattr(event, 'item', None)
|
|
93
93
|
it = getattr(item, 'type', '') if item else ''
|
|
94
94
|
if it == 'function_call':
|
|
95
|
+
self.on_first_token()
|
|
95
96
|
self.output_features.add(LlmFeature.TOOL_USE)
|
|
96
97
|
call_id = getattr(item, 'call_id', '') or getattr(item, 'id', '')
|
|
97
98
|
if call_id and call_id not in self.tool_calls:
|
|
98
99
|
self.tool_calls[call_id] = {"id": call_id, "function": getattr(item, 'name', ''), "arguments": ""}
|
|
99
100
|
elif 'web_search' in it:
|
|
101
|
+
self.on_first_token()
|
|
100
102
|
self.output_features.add(LlmFeature.WEBSEARCH)
|
|
101
103
|
elif t == 'response.function_call_arguments.delta':
|
|
102
104
|
call_id = getattr(event, 'call_id', '')
|
|
103
105
|
if call_id in self.tool_calls:
|
|
106
|
+
self.on_first_token()
|
|
104
107
|
self.tool_calls[call_id]["arguments"] += getattr(event, 'delta', '') or ''
|
|
105
108
|
elif t == 'response.function_call_arguments.done':
|
|
106
109
|
call_id = getattr(event, 'call_id', '')
|
|
@@ -5,6 +5,7 @@ on_chunk()로 청크 데이터를 누적하고, _apply()로 pack에 반영한다
|
|
|
5
5
|
"""
|
|
6
6
|
import time
|
|
7
7
|
|
|
8
|
+
from whatap import logging
|
|
8
9
|
from whatap.llm.providers.interceptor import _dispatch, _ensure_end
|
|
9
10
|
|
|
10
11
|
|
|
@@ -35,7 +36,11 @@ class StreamAccumulator(object):
|
|
|
35
36
|
self.first_token_time = time.monotonic()
|
|
36
37
|
|
|
37
38
|
def finalize(self):
|
|
38
|
-
"""스트림 종료 시: latency/ttft 계산 → _apply() → 전송 → active 카운터 감소.
|
|
39
|
+
"""스트림 종료 시: latency/ttft 계산 → _apply() → 전송 → active 카운터 감소.
|
|
40
|
+
|
|
41
|
+
계측 실패(_apply/dispatch 예외)가 사용자 스트림으로 전파되면 안 되므로 흡수한다.
|
|
42
|
+
active 카운터 정리(_ensure_end)는 어떤 경우에도 보장한다.
|
|
43
|
+
"""
|
|
39
44
|
try:
|
|
40
45
|
end_time = time.monotonic()
|
|
41
46
|
self.pack.latency = round((end_time - self.pack._start_time) * 1000)
|
|
@@ -44,6 +49,8 @@ class StreamAccumulator(object):
|
|
|
44
49
|
self._apply()
|
|
45
50
|
self.pack.success = True
|
|
46
51
|
_dispatch(self.pack)
|
|
52
|
+
except Exception as e:
|
|
53
|
+
logging.debug('[LLM] stream finalize skipped: %s' % e, extra={'id': 'LLM011'})
|
|
47
54
|
finally:
|
|
48
55
|
if self.active_key:
|
|
49
56
|
_ensure_end(self.pack, self.active_key)
|
|
@@ -54,20 +61,79 @@ class StreamAccumulator(object):
|
|
|
54
61
|
|
|
55
62
|
|
|
56
63
|
def sync_stream(response, acc):
|
|
57
|
-
"""동기 스트림 래퍼. 원본 응답을 그대로 yield하면서 청크를 누적한다.
|
|
64
|
+
"""동기 스트림 래퍼. 원본 응답을 그대로 yield하면서 청크를 누적한다.
|
|
65
|
+
|
|
66
|
+
청크 누적(on_chunk) 실패는 흡수한다 — 계측이 사용자 스트림을 깨면 안 된다.
|
|
67
|
+
"""
|
|
58
68
|
try:
|
|
59
69
|
for chunk in response:
|
|
60
|
-
|
|
70
|
+
try:
|
|
71
|
+
acc.on_chunk(chunk)
|
|
72
|
+
except Exception as e:
|
|
73
|
+
logging.debug('[LLM] stream chunk skipped: %s' % e, extra={'id': 'LLM010'})
|
|
61
74
|
yield chunk
|
|
62
75
|
finally:
|
|
63
76
|
acc.finalize()
|
|
64
77
|
|
|
65
78
|
|
|
66
79
|
async def async_stream(response, acc):
|
|
67
|
-
"""비동기 스트림 래퍼. 원본 응답을 그대로 yield하면서 청크를 누적한다.
|
|
80
|
+
"""비동기 스트림 래퍼. 원본 응답을 그대로 yield하면서 청크를 누적한다.
|
|
81
|
+
|
|
82
|
+
청크 누적(on_chunk) 실패는 흡수한다 — 계측이 사용자 스트림을 깨면 안 된다.
|
|
83
|
+
"""
|
|
68
84
|
try:
|
|
69
85
|
async for chunk in response:
|
|
70
|
-
|
|
86
|
+
try:
|
|
87
|
+
acc.on_chunk(chunk)
|
|
88
|
+
except Exception as e:
|
|
89
|
+
logging.debug('[LLM] stream chunk skipped: %s' % e, extra={'id': 'LLM010'})
|
|
71
90
|
yield chunk
|
|
72
91
|
finally:
|
|
73
92
|
acc.finalize()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _wrap_via_parse(response, acc, wrapper, iter_attr):
|
|
96
|
+
"""with_raw_response 래퍼(LegacyAPIResponse)를 호환 처리한다.
|
|
97
|
+
|
|
98
|
+
litellm 은 스트리밍 시 ``client.chat.completions.with_raw_response.create(...)`` 로
|
|
99
|
+
헤더를 뽑은 뒤 ``raw_response.parse()`` 로 실제 스트림을 꺼낸다. 이 경로의 응답은
|
|
100
|
+
스트림 이터러블이 아니라 ``parse()`` 를 가진 래퍼이므로, 그대로 ``async_stream`` 으로
|
|
101
|
+
감싸면 litellm 의 ``raw_response.parse()`` 가 'async_generator has no attribute parse'
|
|
102
|
+
로 터진다. 여기서는 ``parse()`` 가 돌려줄 실제 스트림만 계측 래퍼로 감싸도록
|
|
103
|
+
``parse`` 를 래핑하고 원본을 그대로 돌려준다.
|
|
104
|
+
|
|
105
|
+
반환: (호출자가 돌려줄 객체, 계측 소유 여부). 감쌀 수 없으면 (response, False).
|
|
106
|
+
"""
|
|
107
|
+
parse = getattr(response, "parse", None)
|
|
108
|
+
if not callable(parse):
|
|
109
|
+
return response, False
|
|
110
|
+
|
|
111
|
+
state = {}
|
|
112
|
+
|
|
113
|
+
def instrumented_parse(*args, **kwargs):
|
|
114
|
+
real = parse(*args, **kwargs)
|
|
115
|
+
if hasattr(real, iter_attr):
|
|
116
|
+
if "g" not in state:
|
|
117
|
+
state["g"] = wrapper(real, acc)
|
|
118
|
+
return state["g"]
|
|
119
|
+
return real
|
|
120
|
+
|
|
121
|
+
try:
|
|
122
|
+
response.parse = instrumented_parse
|
|
123
|
+
return response, True
|
|
124
|
+
except Exception:
|
|
125
|
+
return response, False
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def wrap_sync_stream(response, acc):
|
|
129
|
+
"""동기 스트리밍 응답을 계측 래퍼로 감싼다. 반환: (돌려줄 객체, 계측 소유 여부)."""
|
|
130
|
+
if hasattr(response, "__iter__"):
|
|
131
|
+
return sync_stream(response, acc), True
|
|
132
|
+
return _wrap_via_parse(response, acc, sync_stream, "__iter__")
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def wrap_async_stream(response, acc):
|
|
136
|
+
"""비동기 스트리밍 응답을 계측 래퍼로 감싼다. 반환: (돌려줄 객체, 계측 소유 여부)."""
|
|
137
|
+
if hasattr(response, "__aiter__"):
|
|
138
|
+
return async_stream(response, acc), True
|
|
139
|
+
return _wrap_via_parse(response, acc, async_stream, "__aiter__")
|
|
@@ -2,10 +2,10 @@ whatap/LICENSE,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
2
2
|
whatap/README.rst,sha256=M4dohffaOQQcSlo02OgWplEr9SlffyjV3v7w9vHf1RI,967
|
|
3
3
|
whatap/__init__.py,sha256=tuUaSBg5PFasSmn3wUJASnPccQnSa0xOp0xq5BEdMLQ,32665
|
|
4
4
|
whatap/__main__.py,sha256=9fgK-V2eh7rZQ0OKWjpYsWRCyhOx35zRK6XUvJXWuUo,64
|
|
5
|
-
whatap/build.py,sha256=
|
|
5
|
+
whatap/build.py,sha256=hKoNW8VIOOedHEmBLuaSxYWeJmPPBnrPCTza2KD885U,82
|
|
6
6
|
whatap/whatap.conf,sha256=E3UACbCDQ3-3XgDJWovbAtEN6LVLa7BQuCA0NXtX3CY,457
|
|
7
|
-
whatap/agent/darwin/amd64/whatap_python,sha256=
|
|
8
|
-
whatap/agent/darwin/arm64/whatap_python,sha256=
|
|
7
|
+
whatap/agent/darwin/amd64/whatap_python,sha256=dLdQ2aVjuinboro4S8600b4sEA73RpFIHw72lvkSvRk,20802384
|
|
8
|
+
whatap/agent/darwin/arm64/whatap_python,sha256=ar2scLqop8rVxB_ITdOrSIMlyTThQ6rF1V6pf1acqag,19784050
|
|
9
9
|
whatap/agent/linux/amd64/whatap_python,sha256=kt0etbV-7uxNXD3mzrAeVhM2oxwim1N0OtbpkJJB2vg,18905944
|
|
10
10
|
whatap/agent/linux/arm64/whatap_python,sha256=7V7bcI9mELhE1vRPmSQcBBCoRTp2VbBXIKeq-ixTrm4,18780673
|
|
11
11
|
whatap/agent/windows/whatap_python.exe,sha256=ZmZIlcLD8yecsTOlqR6SBnLVQvlbtN70Wv0bb83j8f4,30576618
|
|
@@ -58,31 +58,31 @@ whatap/llm/log_sink_packs/llm_tool_calls.py,sha256=CIYbIsjUQWBr4qetAfLNAVjIFnv-l
|
|
|
58
58
|
whatap/llm/log_sink_packs/llm_tool_results.py,sha256=bbg71I_p6ICVsoqMyIJJ-dpvuhjaeAe7Go3JHYPuxB0,465
|
|
59
59
|
whatap/llm/log_sink_packs/llm_tx_status.py,sha256=m-bSncZO7hD0sa5L7_xRxM9izY9f5k_WOr3Z6JcfEDI,3863
|
|
60
60
|
whatap/llm/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
61
|
-
whatap/llm/providers/interceptor.py,sha256=
|
|
62
|
-
whatap/llm/providers/stream_accumulator.py,sha256=
|
|
61
|
+
whatap/llm/providers/interceptor.py,sha256=e_zel79upmedpRYF8p-EEIlpDv-6SMsD_PSBxsXKfyM,8952
|
|
62
|
+
whatap/llm/providers/stream_accumulator.py,sha256=9AZ0yepp0q9y-p4QiWSRSpAW3jXI2tDoQtpfXo9AnWA,5353
|
|
63
63
|
whatap/llm/providers/anthropic/__init__.py,sha256=OT75EbX5Wly5TrzQbwJsPXo1_wmDZTKm13LuLPwL90w,1595
|
|
64
64
|
whatap/llm/providers/anthropic/messages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
65
|
-
whatap/llm/providers/anthropic/messages/messages.py,sha256=
|
|
65
|
+
whatap/llm/providers/anthropic/messages/messages.py,sha256=GSSko_KjoJGpNdjP3sH0yq8K0ESmYNZTTA9qpu17PPM,3336
|
|
66
66
|
whatap/llm/providers/anthropic/messages/messages_context.py,sha256=Hbl66Hgea514ygMEYUwIs5JbFQfpmpGx3IP-FHWTDoY,2736
|
|
67
|
-
whatap/llm/providers/anthropic/messages/messages_extractor.py,sha256=
|
|
67
|
+
whatap/llm/providers/anthropic/messages/messages_extractor.py,sha256=Iy5Zrl9D7OZJzND1CxSYiSoy2WWIQP3W2htBQa-fi-0,5587
|
|
68
68
|
whatap/llm/providers/openai/__init__.py,sha256=XBU4X6FLhKVS-XhXiUfjXM-TZTJP-4Z155yyBlw-6fc,6408
|
|
69
69
|
whatap/llm/providers/openai/content_parser.py,sha256=0b72UMfzVDnof66-cY6ELeCbqngUXsNhfO4BxRl9UAY,1484
|
|
70
70
|
whatap/llm/providers/openai/chat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
71
|
-
whatap/llm/providers/openai/chat/chat.py,sha256=
|
|
71
|
+
whatap/llm/providers/openai/chat/chat.py,sha256=0zws4O84wXN1bHjghdU7r4icB9HMn2RS4-PFWfjPh9w,3939
|
|
72
72
|
whatap/llm/providers/openai/chat/chat_context.py,sha256=5hdZq_pYXwAaJtX28LaG5sNp002HXPzMlwgL0jhYy3Q,2947
|
|
73
|
-
whatap/llm/providers/openai/chat/chat_extractor.py,sha256=
|
|
73
|
+
whatap/llm/providers/openai/chat/chat_extractor.py,sha256=ubPoi6CKGvuNgkv40eVrzvLGIRMBFWUFu7nnpLb6T0g,5135
|
|
74
74
|
whatap/llm/providers/openai/completions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
75
|
-
whatap/llm/providers/openai/completions/completions.py,sha256=
|
|
75
|
+
whatap/llm/providers/openai/completions/completions.py,sha256=N5DO40p8VbKlr71bRg3ptRxI5rV6K7NjKUNYQA88oGM,3247
|
|
76
76
|
whatap/llm/providers/openai/completions/completions_context.py,sha256=cYA2F2gjZUaLsOgbjkFQcDkE_nJIDr5UgayvxyGFixk,917
|
|
77
77
|
whatap/llm/providers/openai/completions/completions_extractor.py,sha256=uCW05rou1zwQPuTifOmv-987nW_WoXZ4WKoJTmeD4TE,1934
|
|
78
78
|
whatap/llm/providers/openai/embeddings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
79
|
-
whatap/llm/providers/openai/embeddings/embeddings.py,sha256=
|
|
79
|
+
whatap/llm/providers/openai/embeddings/embeddings.py,sha256=8AJ2RlJYCOdbBuEuV2QVbAWbBqlZ2QLAXJEzruOv21U,2735
|
|
80
80
|
whatap/llm/providers/openai/embeddings/embeddings_context.py,sha256=Knu3q9zI5Ph5rQUeALl-ZvaXRfvvbSoaOvCTAjdKb_g,793
|
|
81
81
|
whatap/llm/providers/openai/embeddings/embeddings_extractor.py,sha256=Qzjt4SHmuLfSGECr8nt6hVj8R-SkeQfqkXY_xUz_VSg,983
|
|
82
82
|
whatap/llm/providers/openai/responses/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
83
|
-
whatap/llm/providers/openai/responses/responses.py,sha256=
|
|
83
|
+
whatap/llm/providers/openai/responses/responses.py,sha256=GKdwOuRpjT5ePxfMwgLL32odJdJ8UWSX6vusM_JL4m8,3452
|
|
84
84
|
whatap/llm/providers/openai/responses/responses_context.py,sha256=8ZzqEbG4LFi8nhHggMkx7xoL3HNV8_HVShyqD7BSnmc,3308
|
|
85
|
-
whatap/llm/providers/openai/responses/responses_extractor.py,sha256=
|
|
85
|
+
whatap/llm/providers/openai/responses/responses_extractor.py,sha256=HinUvPiQplM_lRUiR694a5DJOmCA8RlPJztdvaayQ6k,5500
|
|
86
86
|
whatap/llm/stats/__init__.py,sha256=uR_KiY5K_dwRyHX1u1961TY0hy94LJE0_hmueVgRfgo,1352
|
|
87
87
|
whatap/llm/stats/active_stat.py,sha256=lPyfy_pBq1CM8ABXHU7bMmTXKu2CxXIAOhScfqOKC9s,2917
|
|
88
88
|
whatap/llm/stats/answer_relevance_eval_stat.py,sha256=rel9_fdH5_90qQ4AZy01Qv8plorQsWmB-4A3XwJ3oFw,353
|
|
@@ -220,8 +220,8 @@ whatap/value/text_hash_value.py,sha256=WPsSWzkR17plw3sgGjhJK0heCQiy0XdDYhawvBvQl
|
|
|
220
220
|
whatap/value/text_value.py,sha256=8CWF57POGgosr5zEnGc0BdY0pYF0Vh8EsifHaRgfEwU,1148
|
|
221
221
|
whatap/value/value.py,sha256=puRYlGm7q0iX3TKOqe_e0NdgK9Zj2J1t7NzqaQ8g7bU,578
|
|
222
222
|
whatap/value/value_enum.py,sha256=AoUU4HUWC1bPan8k8CRBE9ayl0rGvJJHdRCTsamkWxE,2577
|
|
223
|
-
whatap_python-2.1.
|
|
224
|
-
whatap_python-2.1.
|
|
225
|
-
whatap_python-2.1.
|
|
226
|
-
whatap_python-2.1.
|
|
227
|
-
whatap_python-2.1.
|
|
223
|
+
whatap_python-2.1.1.dist-info/METADATA,sha256=0arI4JATShXzF1jtoYJ6-5xybdFQ1PqZAErvzxlu_yA,2244
|
|
224
|
+
whatap_python-2.1.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
225
|
+
whatap_python-2.1.1.dist-info/entry_points.txt,sha256=lAwpO3oqmPt5riREZO759dHWoM8dqv-vMpDWwDcMiJY,280
|
|
226
|
+
whatap_python-2.1.1.dist-info/top_level.txt,sha256=8bRV-cNhEi4cSEIRQm5zDLRqkH1ki9YVrcfLIUVs5jg,7
|
|
227
|
+
whatap_python-2.1.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|