ctxoptimizer 0.4.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.
- ctxoptimizer/__init__.py +5 -0
- ctxoptimizer/__main__.py +14 -0
- ctxoptimizer/admin_security.py +130 -0
- ctxoptimizer/analysis/__init__.py +1 -0
- ctxoptimizer/analysis/baseline.py +69 -0
- ctxoptimizer/analysis/break_even.py +49 -0
- ctxoptimizer/analysis/drift.py +151 -0
- ctxoptimizer/analysis/edits.py +148 -0
- ctxoptimizer/analysis/markers.py +34 -0
- ctxoptimizer/analysis/recommend.py +114 -0
- ctxoptimizer/analysis/segments.py +57 -0
- ctxoptimizer/analysis/session.py +62 -0
- ctxoptimizer/analysis/state.py +239 -0
- ctxoptimizer/analysis/tokens.py +62 -0
- ctxoptimizer/analysis/waste.py +322 -0
- ctxoptimizer/cli.py +873 -0
- ctxoptimizer/config.py +512 -0
- ctxoptimizer/dashboard/__init__.py +1 -0
- ctxoptimizer/dashboard/api.py +537 -0
- ctxoptimizer/dashboard/service.py +157 -0
- ctxoptimizer/dashboard/static/.gitkeep +1 -0
- ctxoptimizer/dashboard/templates/.gitkeep +1 -0
- ctxoptimizer/dashboard/templates/doctor.html +46 -0
- ctxoptimizer/dashboard/templates/live.html +91 -0
- ctxoptimizer/dashboard/templates/overview.html +172 -0
- ctxoptimizer/dashboard/templates/pricing.html +115 -0
- ctxoptimizer/dashboard/templates/request.html +111 -0
- ctxoptimizer/dashboard/templates/session.html +67 -0
- ctxoptimizer/dashboard/templates/settings.html +79 -0
- ctxoptimizer/dashboard/templates/table.html +56 -0
- ctxoptimizer/dashboard/templates/transforms.html +118 -0
- ctxoptimizer/dashboard/templates/waste.html +120 -0
- ctxoptimizer/dashboard/views.py +429 -0
- ctxoptimizer/data/cache_limits.json +23 -0
- ctxoptimizer/data/default_config.toml +185 -0
- ctxoptimizer/data/pricing.json +421 -0
- ctxoptimizer/doctor.py +129 -0
- ctxoptimizer/guard.py +139 -0
- ctxoptimizer/ids.py +73 -0
- ctxoptimizer/log.py +73 -0
- ctxoptimizer/mcp.py +124 -0
- ctxoptimizer/models.py +145 -0
- ctxoptimizer/pipeline.py +126 -0
- ctxoptimizer/profiles.py +152 -0
- ctxoptimizer/providers/__init__.py +37 -0
- ctxoptimizer/providers/anthropic.py +105 -0
- ctxoptimizer/providers/base.py +191 -0
- ctxoptimizer/providers/gemini.py +143 -0
- ctxoptimizer/providers/ollama.py +133 -0
- ctxoptimizer/providers/openai_chat.py +135 -0
- ctxoptimizer/providers/openai_responses.py +121 -0
- ctxoptimizer/proxy.py +665 -0
- ctxoptimizer/routing.py +259 -0
- ctxoptimizer/server.py +319 -0
- ctxoptimizer/service.py +150 -0
- ctxoptimizer/telemetry/__init__.py +1 -0
- ctxoptimizer/telemetry/blobs.py +57 -0
- ctxoptimizer/telemetry/expand.py +62 -0
- ctxoptimizer/telemetry/pricing.py +168 -0
- ctxoptimizer/telemetry/redact.py +53 -0
- ctxoptimizer/telemetry/sse.py +145 -0
- ctxoptimizer/telemetry/store.py +829 -0
- ctxoptimizer/telemetry/usage.py +435 -0
- ctxoptimizer/transforms/__init__.py +87 -0
- ctxoptimizer/transforms/ansi.py +21 -0
- ctxoptimizer/transforms/anthropic_cache.py +100 -0
- ctxoptimizer/transforms/backref.py +74 -0
- ctxoptimizer/transforms/base.py +93 -0
- ctxoptimizer/transforms/cache_serve.py +146 -0
- ctxoptimizer/transforms/dedupe_lines.py +69 -0
- ctxoptimizer/transforms/eligibility.py +24 -0
- ctxoptimizer/transforms/openai_hints.py +84 -0
- ctxoptimizer/transforms/progress.py +52 -0
- ctxoptimizer/transforms/stale_result_elision.py +109 -0
- ctxoptimizer/transforms/truncate.py +100 -0
- ctxoptimizer/transforms/volatile.py +177 -0
- ctxoptimizer/transforms/whitespace.py +25 -0
- ctxoptimizer-0.4.1.dist-info/METADATA +196 -0
- ctxoptimizer-0.4.1.dist-info/RECORD +83 -0
- ctxoptimizer-0.4.1.dist-info/WHEEL +4 -0
- ctxoptimizer-0.4.1.dist-info/entry_points.txt +3 -0
- ctxoptimizer-0.4.1.dist-info/licenses/LICENSE +21 -0
- ctxoptimizer-0.4.1.dist-info/licenses/NOTICE +13 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""OpenAI Chat Completions adapter (SPEC section 11.2)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Iterator
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from ctxoptimizer.analysis.segments import Segment, make_segment
|
|
9
|
+
from ctxoptimizer.models import Provider, ToolResultRef
|
|
10
|
+
from ctxoptimizer.providers.base import DictToolResultRef, blocks_of, text_of
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class OpenAIChatAdapter:
|
|
14
|
+
provider = Provider.OPENAI
|
|
15
|
+
|
|
16
|
+
def model_of(self, payload: dict[str, Any]) -> str | None:
|
|
17
|
+
model = payload.get("model")
|
|
18
|
+
return model if isinstance(model, str) else None
|
|
19
|
+
|
|
20
|
+
def is_stream(self, payload: dict[str, Any]) -> bool:
|
|
21
|
+
return bool(payload.get("stream"))
|
|
22
|
+
|
|
23
|
+
def is_transformable(self, payload: dict[str, Any]) -> bool:
|
|
24
|
+
return True
|
|
25
|
+
|
|
26
|
+
def iter_tool_results(self, payload: dict[str, Any]) -> Iterator[ToolResultRef]:
|
|
27
|
+
names = self._tool_call_names(payload)
|
|
28
|
+
for message_index, message in enumerate(payload.get("messages") or []):
|
|
29
|
+
if not isinstance(message, dict) or message.get("role") != "tool":
|
|
30
|
+
continue
|
|
31
|
+
call_id = str(message.get("tool_call_id") or "")
|
|
32
|
+
yield DictToolResultRef(
|
|
33
|
+
message,
|
|
34
|
+
message_index=message_index,
|
|
35
|
+
block_index=None,
|
|
36
|
+
tool_use_id=call_id,
|
|
37
|
+
tool_name=names.get(call_id),
|
|
38
|
+
is_error=False,
|
|
39
|
+
key="content",
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def segments(self, payload: dict[str, Any]) -> list[Segment]:
|
|
43
|
+
result: list[Segment] = []
|
|
44
|
+
for index, tool in enumerate(payload.get("tools") or []):
|
|
45
|
+
if isinstance(tool, dict):
|
|
46
|
+
result.append(make_segment("tool_def", tool, block_index=index))
|
|
47
|
+
for message_index, message in enumerate(payload.get("messages") or []):
|
|
48
|
+
if not isinstance(message, dict):
|
|
49
|
+
continue
|
|
50
|
+
role = str(message.get("role") or "")
|
|
51
|
+
content = message.get("content")
|
|
52
|
+
if isinstance(content, str):
|
|
53
|
+
result.append(
|
|
54
|
+
make_segment(
|
|
55
|
+
"message",
|
|
56
|
+
{"role": role, "content": content},
|
|
57
|
+
msg_index=message_index,
|
|
58
|
+
block_index=0,
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
continue
|
|
62
|
+
if content is None:
|
|
63
|
+
tool_calls = message.get("tool_calls")
|
|
64
|
+
if isinstance(tool_calls, list) and tool_calls:
|
|
65
|
+
result.append(
|
|
66
|
+
make_segment(
|
|
67
|
+
"message",
|
|
68
|
+
{"role": role, "tool_calls": tool_calls},
|
|
69
|
+
msg_index=message_index,
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
continue
|
|
73
|
+
for block_index, block in enumerate(blocks_of(content)):
|
|
74
|
+
result.append(
|
|
75
|
+
make_segment(
|
|
76
|
+
"message",
|
|
77
|
+
{"role": role, **block},
|
|
78
|
+
msg_index=message_index,
|
|
79
|
+
block_index=block_index,
|
|
80
|
+
)
|
|
81
|
+
)
|
|
82
|
+
return result
|
|
83
|
+
|
|
84
|
+
def system_text(self, payload: dict[str, Any]) -> str:
|
|
85
|
+
parts = [
|
|
86
|
+
text_of(message.get("content"))
|
|
87
|
+
for message in payload.get("messages") or []
|
|
88
|
+
if isinstance(message, dict) and message.get("role") in {"system", "developer"}
|
|
89
|
+
]
|
|
90
|
+
return "\n".join(part for part in parts if part)
|
|
91
|
+
|
|
92
|
+
def first_user_text(self, payload: dict[str, Any]) -> str:
|
|
93
|
+
for message in payload.get("messages") or []:
|
|
94
|
+
if isinstance(message, dict) and message.get("role") == "user":
|
|
95
|
+
return text_of(message.get("content"))
|
|
96
|
+
return ""
|
|
97
|
+
|
|
98
|
+
def tool_names(self, payload: dict[str, Any]) -> list[str]:
|
|
99
|
+
names: list[str] = []
|
|
100
|
+
for tool in payload.get("tools") or []:
|
|
101
|
+
if not isinstance(tool, dict):
|
|
102
|
+
continue
|
|
103
|
+
function = tool.get("function")
|
|
104
|
+
if isinstance(function, dict) and isinstance(function.get("name"), str):
|
|
105
|
+
names.append(function["name"])
|
|
106
|
+
return names
|
|
107
|
+
|
|
108
|
+
def used_tool_names(self, payload: dict[str, Any]) -> list[str]:
|
|
109
|
+
names: list[str] = []
|
|
110
|
+
for message in payload.get("messages") or []:
|
|
111
|
+
if not isinstance(message, dict):
|
|
112
|
+
continue
|
|
113
|
+
for call in message.get("tool_calls") or []:
|
|
114
|
+
if not isinstance(call, dict):
|
|
115
|
+
continue
|
|
116
|
+
function = call.get("function")
|
|
117
|
+
if isinstance(function, dict) and isinstance(function.get("name"), str):
|
|
118
|
+
names.append(function["name"])
|
|
119
|
+
return names
|
|
120
|
+
|
|
121
|
+
def _tool_call_names(self, payload: dict[str, Any]) -> dict[str, str]:
|
|
122
|
+
names: dict[str, str] = {}
|
|
123
|
+
for message in payload.get("messages") or []:
|
|
124
|
+
if not isinstance(message, dict):
|
|
125
|
+
continue
|
|
126
|
+
for call in message.get("tool_calls") or []:
|
|
127
|
+
if not isinstance(call, dict):
|
|
128
|
+
continue
|
|
129
|
+
function = call.get("function")
|
|
130
|
+
if isinstance(call.get("id"), str) and isinstance(function, dict):
|
|
131
|
+
names[call["id"]] = str(function.get("name") or "")
|
|
132
|
+
return names
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
__all__ = ["OpenAIChatAdapter"]
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""OpenAI Responses adapter (SPEC section 11.3)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Iterator
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from ctxoptimizer.analysis.segments import Segment, make_segment
|
|
9
|
+
from ctxoptimizer.models import Provider, ToolResultRef
|
|
10
|
+
from ctxoptimizer.providers.base import DictToolResultRef, blocks_of, text_of
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class OpenAIResponsesAdapter:
|
|
14
|
+
provider = Provider.OPENAI
|
|
15
|
+
|
|
16
|
+
def model_of(self, payload: dict[str, Any]) -> str | None:
|
|
17
|
+
model = payload.get("model")
|
|
18
|
+
return model if isinstance(model, str) else None
|
|
19
|
+
|
|
20
|
+
def is_stream(self, payload: dict[str, Any]) -> bool:
|
|
21
|
+
return bool(payload.get("stream"))
|
|
22
|
+
|
|
23
|
+
def is_transformable(self, payload: dict[str, Any]) -> bool:
|
|
24
|
+
return not bool(payload.get("previous_response_id"))
|
|
25
|
+
|
|
26
|
+
def iter_tool_results(self, payload: dict[str, Any]) -> Iterator[ToolResultRef]:
|
|
27
|
+
names = self._call_names(payload)
|
|
28
|
+
for message_index, item in enumerate(self._input_items(payload)):
|
|
29
|
+
if not isinstance(item, dict) or item.get("type") != "function_call_output":
|
|
30
|
+
continue
|
|
31
|
+
call_id = str(item.get("call_id") or "")
|
|
32
|
+
yield DictToolResultRef(
|
|
33
|
+
item,
|
|
34
|
+
message_index=message_index,
|
|
35
|
+
block_index=None,
|
|
36
|
+
tool_use_id=call_id,
|
|
37
|
+
tool_name=names.get(call_id),
|
|
38
|
+
is_error=False,
|
|
39
|
+
key="output",
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
def segments(self, payload: dict[str, Any]) -> list[Segment]:
|
|
43
|
+
result: list[Segment] = []
|
|
44
|
+
instructions = payload.get("instructions")
|
|
45
|
+
if isinstance(instructions, str) and instructions:
|
|
46
|
+
result.append(
|
|
47
|
+
make_segment(
|
|
48
|
+
"system",
|
|
49
|
+
{"type": "text", "text": instructions},
|
|
50
|
+
block_index=0,
|
|
51
|
+
)
|
|
52
|
+
)
|
|
53
|
+
for index, tool in enumerate(payload.get("tools") or []):
|
|
54
|
+
if isinstance(tool, dict):
|
|
55
|
+
result.append(make_segment("tool_def", tool, block_index=index))
|
|
56
|
+
for message_index, item in enumerate(self._input_items(payload)):
|
|
57
|
+
if not isinstance(item, dict):
|
|
58
|
+
continue
|
|
59
|
+
if item.get("type") in {"message", None} and item.get("role"):
|
|
60
|
+
role = str(item["role"])
|
|
61
|
+
content = item.get("content")
|
|
62
|
+
if isinstance(content, str):
|
|
63
|
+
result.append(
|
|
64
|
+
make_segment(
|
|
65
|
+
"message",
|
|
66
|
+
{"role": role, "content": content},
|
|
67
|
+
msg_index=message_index,
|
|
68
|
+
block_index=0,
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
continue
|
|
72
|
+
for block_index, block in enumerate(blocks_of(content)):
|
|
73
|
+
result.append(
|
|
74
|
+
make_segment(
|
|
75
|
+
"message",
|
|
76
|
+
{"role": role, **block},
|
|
77
|
+
msg_index=message_index,
|
|
78
|
+
block_index=block_index,
|
|
79
|
+
)
|
|
80
|
+
)
|
|
81
|
+
else:
|
|
82
|
+
result.append(make_segment("message", item, msg_index=message_index))
|
|
83
|
+
return result
|
|
84
|
+
|
|
85
|
+
def system_text(self, payload: dict[str, Any]) -> str:
|
|
86
|
+
return payload["instructions"] if isinstance(payload.get("instructions"), str) else ""
|
|
87
|
+
|
|
88
|
+
def first_user_text(self, payload: dict[str, Any]) -> str:
|
|
89
|
+
for item in self._input_items(payload):
|
|
90
|
+
if isinstance(item, dict) and item.get("role") == "user":
|
|
91
|
+
return text_of(item.get("content"))
|
|
92
|
+
return ""
|
|
93
|
+
|
|
94
|
+
def tool_names(self, payload: dict[str, Any]) -> list[str]:
|
|
95
|
+
return [
|
|
96
|
+
str(tool["name"])
|
|
97
|
+
for tool in payload.get("tools") or []
|
|
98
|
+
if isinstance(tool, dict) and isinstance(tool.get("name"), str)
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
def used_tool_names(self, payload: dict[str, Any]) -> list[str]:
|
|
102
|
+
return list(self._call_names(payload).values())
|
|
103
|
+
|
|
104
|
+
def _input_items(self, payload: dict[str, Any]) -> list[Any]:
|
|
105
|
+
value = payload.get("input")
|
|
106
|
+
if isinstance(value, str):
|
|
107
|
+
return [{"type": "message", "role": "user", "content": value}]
|
|
108
|
+
return value if isinstance(value, list) else []
|
|
109
|
+
|
|
110
|
+
def _call_names(self, payload: dict[str, Any]) -> dict[str, str]:
|
|
111
|
+
names: dict[str, str] = {}
|
|
112
|
+
for item in self._input_items(payload):
|
|
113
|
+
if isinstance(item, dict) and item.get("type") == "function_call":
|
|
114
|
+
call_id = item.get("call_id")
|
|
115
|
+
name = item.get("name")
|
|
116
|
+
if isinstance(call_id, str) and isinstance(name, str):
|
|
117
|
+
names[call_id] = name
|
|
118
|
+
return names
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
__all__ = ["OpenAIResponsesAdapter"]
|