@miller-tech/uap 1.20.34 → 1.20.36
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.
- package/README.md +1 -0
- package/docs/deployment/QWEN35_LLAMA_CPP.md +15 -6
- package/package.json +1 -1
- package/tools/agents/config/qwen3.5-enhanced.jinja +187 -0
- package/tools/agents/scripts/anthropic_proxy.py +1097 -59
- package/tools/agents/scripts/tool-choice-proxy.cjs +12 -0
- package/tools/agents/tests/test_anthropic_proxy_streaming.py +193 -8
package/README.md
CHANGED
|
@@ -77,6 +77,7 @@ uap setup -p all
|
|
|
77
77
|
| CLI | 25 commands | Full system management with rich dashboard visualization |
|
|
78
78
|
| Benchmarks | 9 modules | Terminal-Bench adapter, Harbor integration, A/B comparison |
|
|
79
79
|
| LLM Optimization | 5 tools | Qwen3.5 tool call fixes, llama.cpp optimizer, LoRA training |
|
|
80
|
+
| Local LLM Proxy | 1 service | Anthropic Messages API default; OpenAI Chat Completions retained as option |
|
|
80
81
|
| RTK | 1 module | 60-90% token savings on command outputs |
|
|
81
82
|
| Platforms | 9 integrations | Claude, Factory, OpenCode, ForgeCode, VSCode, Beads, Codex, Pipeline, OMP |
|
|
82
83
|
|
|
@@ -269,12 +269,21 @@ export ANTHROPIC_BASE_URL=http://localhost:4000
|
|
|
269
269
|
|
|
270
270
|
### Endpoints
|
|
271
271
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
272
|
+
The proxy speaks **Anthropic Messages API as its canonical interface** and
|
|
273
|
+
keeps an **OpenAI Chat Completions passthrough** for clients that require the
|
|
274
|
+
OpenAI shape. Both paths run through the same guarded pipeline (loop
|
|
275
|
+
detection, tool narrowing, malformed-payload retry, context pruning, etc.) —
|
|
276
|
+
the OpenAI route converts the request to Anthropic, runs the pipeline, and
|
|
277
|
+
re-shapes the final response back to OpenAI.
|
|
278
|
+
|
|
279
|
+
| Path | Method | Shape | Description |
|
|
280
|
+
| ------------------------ | ------ | --------- | --------------------------------------------------------------- |
|
|
281
|
+
| `/v1/messages` | POST | Anthropic | Anthropic Messages API — default/canonical (streaming + sync) |
|
|
282
|
+
| `/anthropic/v1/messages` | POST | Anthropic | Alias for `/v1/messages` (some Claude Code configs use this) |
|
|
283
|
+
| `/v1/chat/completions` | POST | OpenAI | OpenAI Chat Completions passthrough (e.g. Forge, OpenCode) |
|
|
284
|
+
| `/v1/models` | GET | Anthropic | Lists spoofed Anthropic model IDs |
|
|
285
|
+
| `/health` | GET | — | Health check (verifies upstream reachability) |
|
|
286
|
+
| `/v1/context` | GET | — | Current session context usage and pruning state |
|
|
278
287
|
|
|
279
288
|
### Running as a Service (systemd)
|
|
280
289
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
{%- set image_count = namespace(value=0) %}
|
|
2
|
+
{%- set video_count = namespace(value=0) %}
|
|
3
|
+
{%- macro render_content(content, do_vision_count, is_system_content=false) %}
|
|
4
|
+
{%- if content is string %}
|
|
5
|
+
{{- content }}
|
|
6
|
+
{%- elif content is iterable and content is not mapping %}
|
|
7
|
+
{%- for item in content %}
|
|
8
|
+
{%- if 'image' in item or 'image_url' in item or item.type == 'image' %}
|
|
9
|
+
{%- if is_system_content %}
|
|
10
|
+
{{- raise_exception('System message cannot contain images.') }}
|
|
11
|
+
{%- endif %}
|
|
12
|
+
{%- if do_vision_count %}
|
|
13
|
+
{%- set image_count.value = image_count.value + 1 %}
|
|
14
|
+
{%- endif %}
|
|
15
|
+
{%- if add_vision_id is defined and add_vision_id %}
|
|
16
|
+
{{- 'Picture ' ~ image_count.value ~ ': ' }}
|
|
17
|
+
{%- endif %}
|
|
18
|
+
{{- '<|vision_start|><|image_pad|><|vision_end|>' }}
|
|
19
|
+
{%- elif 'video' in item or item.type == 'video' %}
|
|
20
|
+
{%- if is_system_content %}
|
|
21
|
+
{{- raise_exception('System message cannot contain videos.') }}
|
|
22
|
+
{%- endif %}
|
|
23
|
+
{%- if do_vision_count %}
|
|
24
|
+
{%- set video_count.value = video_count.value + 1 %}
|
|
25
|
+
{%- endif %}
|
|
26
|
+
{%- if add_vision_id is defined and add_vision_id %}
|
|
27
|
+
{{- 'Video ' ~ video_count.value ~ ': ' }}
|
|
28
|
+
{%- endif %}
|
|
29
|
+
{{- '<|vision_start|><|video_pad|><|vision_end|>' }}
|
|
30
|
+
{%- elif 'text' in item %}
|
|
31
|
+
{{- item.text }}
|
|
32
|
+
{%- else %}
|
|
33
|
+
{{- raise_exception('Unexpected item type in content.') }}
|
|
34
|
+
{%- endif %}
|
|
35
|
+
{%- endfor %}
|
|
36
|
+
{%- elif content is none or content is undefined %}
|
|
37
|
+
{{- '' }}
|
|
38
|
+
{%- else %}
|
|
39
|
+
{{- raise_exception('Unexpected content type.') }}
|
|
40
|
+
{%- endif %}
|
|
41
|
+
{%- endmacro %}
|
|
42
|
+
{%- set ns_flags = namespace(enable_thinking=true) %}
|
|
43
|
+
{%- if enable_thinking is defined %}
|
|
44
|
+
{%- set ns_flags.enable_thinking = enable_thinking %}
|
|
45
|
+
{%- endif %}
|
|
46
|
+
{%- if not messages %}
|
|
47
|
+
{{- raise_exception('No messages provided.') }}
|
|
48
|
+
{%- endif %}
|
|
49
|
+
{%- if tools and tools is iterable and tools is not mapping %}
|
|
50
|
+
{{- '<|im_start|>system\n' }}
|
|
51
|
+
{{- "# Tools\n\nYou have access to the following functions:\n\n<tools>" }}
|
|
52
|
+
{%- for tool in tools %}
|
|
53
|
+
{{- "\n" }}
|
|
54
|
+
{{- tool | tojson }}
|
|
55
|
+
{%- endfor %}
|
|
56
|
+
{{- "\n</tools>" }}
|
|
57
|
+
{{- '\n\nIf you choose to call a function ONLY reply in the following format with NO suffix:\n\n<tool_call>\n<function=example_function_name>\n<parameter=example_parameter_1>\nvalue_1\n</parameter>\n<parameter=example_parameter_2>\nThis is the value for the second parameter\nthat can span\nmultiple lines\n</parameter>\n</function>\n</tool_call>\n\n<IMPORTANT>\nReminder:\n- Function calls MUST follow the specified format: an inner <function=...></function> block must be nested within <tool_call></tool_call> XML tags\n- Required parameters MUST be specified\n- You may provide optional reasoning for your function call in natural language BEFORE the function call, but NOT after\n- If there is no function call available, answer the question like normal with your current knowledge and do not tell the user about function calls\n</IMPORTANT>' }}
|
|
58
|
+
{%- if messages[0].role == 'system' or messages[0].role == 'developer' %}
|
|
59
|
+
{%- set content = render_content(messages[0].content, false, true)|trim %}
|
|
60
|
+
{%- if '<|think_off|>' in content %}
|
|
61
|
+
{%- set ns_flags.enable_thinking = false %}
|
|
62
|
+
{%- set content = content.replace('<|think_off|>', '').strip() %}
|
|
63
|
+
{%- elif '<|think_on|>' in content %}
|
|
64
|
+
{%- set ns_flags.enable_thinking = true %}
|
|
65
|
+
{%- set content = content.replace('<|think_on|>', '').strip() %}
|
|
66
|
+
{%- endif %}
|
|
67
|
+
{%- if content %}
|
|
68
|
+
{{- '\n\n' + content }}
|
|
69
|
+
{%- endif %}
|
|
70
|
+
{%- endif %}
|
|
71
|
+
{{- '<|im_end|>\n' }}
|
|
72
|
+
{%- else %}
|
|
73
|
+
{%- if messages[0].role == 'system' or messages[0].role == 'developer' %}
|
|
74
|
+
{%- set content = render_content(messages[0].content, false, true)|trim %}
|
|
75
|
+
{%- if '<|think_off|>' in content %}
|
|
76
|
+
{%- set ns_flags.enable_thinking = false %}
|
|
77
|
+
{%- set content = content.replace('<|think_off|>', '').strip() %}
|
|
78
|
+
{%- elif '<|think_on|>' in content %}
|
|
79
|
+
{%- set ns_flags.enable_thinking = true %}
|
|
80
|
+
{%- set content = content.replace('<|think_on|>', '').strip() %}
|
|
81
|
+
{%- endif %}
|
|
82
|
+
{{- '<|im_start|>system\n' + content + '<|im_end|>\n' }}
|
|
83
|
+
{%- endif %}
|
|
84
|
+
{%- endif %}
|
|
85
|
+
{%- set ns = namespace(multi_step_tool=true, last_query_index=messages|length - 1) %}
|
|
86
|
+
{%- for message in messages[::-1] %}
|
|
87
|
+
{%- set index = (messages|length - 1) - loop.index0 %}
|
|
88
|
+
{%- if ns.multi_step_tool and message.role == "user" %}
|
|
89
|
+
{%- set content = render_content(message.content, false)|trim %}
|
|
90
|
+
{%- if not(content.startswith('<tool_response>') and content.endswith('</tool_response>')) %}
|
|
91
|
+
{%- set ns.multi_step_tool = false %}
|
|
92
|
+
{%- set ns.last_query_index = index %}
|
|
93
|
+
{%- endif %}
|
|
94
|
+
{%- endif %}
|
|
95
|
+
{%- endfor %}
|
|
96
|
+
{%- if ns.multi_step_tool %}
|
|
97
|
+
{{- raise_exception('No user query found in messages.') }}
|
|
98
|
+
{%- endif %}
|
|
99
|
+
{%- for message in messages %}
|
|
100
|
+
{%- set content = render_content(message.content, true)|trim %}
|
|
101
|
+
{%- if '<|think_off|>' in content %}
|
|
102
|
+
{%- set ns_flags.enable_thinking = false %}
|
|
103
|
+
{%- set content = content.replace('<|think_off|>', '').strip() %}
|
|
104
|
+
{%- elif '<|think_on|>' in content %}
|
|
105
|
+
{%- set ns_flags.enable_thinking = true %}
|
|
106
|
+
{%- set content = content.replace('<|think_on|>', '').strip() %}
|
|
107
|
+
{%- endif %}
|
|
108
|
+
{%- if message.role == "system" or message.role == "developer" %}
|
|
109
|
+
{%- if not loop.first %}
|
|
110
|
+
{{- raise_exception('System message must be at the beginning.') }}
|
|
111
|
+
{%- endif %}
|
|
112
|
+
{%- elif message.role == "user" %}
|
|
113
|
+
{{- '<|im_start|>' + message.role + '\n' + content + '<|im_end|>' + '\n' }}
|
|
114
|
+
{%- elif message.role == "assistant" %}
|
|
115
|
+
{%- set reasoning_content = '' %}
|
|
116
|
+
{%- if message.reasoning_content is string %}
|
|
117
|
+
{%- set reasoning_content = message.reasoning_content %}
|
|
118
|
+
{%- else %}
|
|
119
|
+
{%- if '</think>' in content %}
|
|
120
|
+
{%- set reasoning_content = content.split('</think>')[0].rstrip('\n').split('<think>')[-1].lstrip('\n') %}
|
|
121
|
+
{%- set content = content.split('</think>')[-1].lstrip('\n') %}
|
|
122
|
+
{%- endif %}
|
|
123
|
+
{%- endif %}
|
|
124
|
+
{%- set reasoning_content = reasoning_content|trim %}
|
|
125
|
+
{%- if loop.index0 > ns.last_query_index and reasoning_content %}
|
|
126
|
+
{{- '<|im_start|>' + message.role + '\n<think>\n' + reasoning_content + '\n</think>\n\n' + content }}
|
|
127
|
+
{%- else %}
|
|
128
|
+
{{- '<|im_start|>' + message.role + '\n' + content }}
|
|
129
|
+
{%- endif %}
|
|
130
|
+
{%- if message.tool_calls and message.tool_calls is iterable and message.tool_calls is not mapping %}
|
|
131
|
+
{%- for tool_call in message.tool_calls %}
|
|
132
|
+
{%- if tool_call.function is defined %}
|
|
133
|
+
{%- set tool_call = tool_call.function %}
|
|
134
|
+
{%- endif %}
|
|
135
|
+
{%- if loop.first %}
|
|
136
|
+
{%- if content|trim %}
|
|
137
|
+
{{- '\n\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
|
138
|
+
{%- else %}
|
|
139
|
+
{{- '<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
|
140
|
+
{%- endif %}
|
|
141
|
+
{%- else %}
|
|
142
|
+
{{- '\n<tool_call>\n<function=' + tool_call.name + '>\n' }}
|
|
143
|
+
{%- endif %}
|
|
144
|
+
{%- if tool_call.arguments is defined and tool_call.arguments is mapping %}
|
|
145
|
+
{%- if tool_call.arguments|length > 0 %}
|
|
146
|
+
{%- for args_name in tool_call.arguments %}
|
|
147
|
+
{%- set args_value = tool_call.arguments[args_name] %}
|
|
148
|
+
{{- '<parameter=' + args_name + '>\n' }}
|
|
149
|
+
{%- set args_value = args_value | tojson if args_value is mapping or (args_value is iterable and args_value is not string) else args_value | string %}
|
|
150
|
+
{{- args_value }}
|
|
151
|
+
{{- '\n</parameter>\n' }}
|
|
152
|
+
{%- endfor %}
|
|
153
|
+
{%- endif %}
|
|
154
|
+
{%- elif tool_call.arguments is defined and tool_call.arguments is string %}
|
|
155
|
+
{%- if tool_call.arguments|trim|length > 0 %}
|
|
156
|
+
{{- tool_call.arguments }}
|
|
157
|
+
{{- '\n' }}
|
|
158
|
+
{%- endif %}
|
|
159
|
+
{%- endif %}
|
|
160
|
+
{{- '</function>\n</tool_call>' }}
|
|
161
|
+
{%- endfor %}
|
|
162
|
+
{%- endif %}
|
|
163
|
+
{{- '<|im_end|>\n' }}
|
|
164
|
+
{%- elif message.role == "tool" %}
|
|
165
|
+
{%- if loop.previtem and loop.previtem.role != "tool" %}
|
|
166
|
+
{{- '<|im_start|>user' }}
|
|
167
|
+
{%- endif %}
|
|
168
|
+
{{- '\n<tool_response>\n' }}
|
|
169
|
+
{{- content }}
|
|
170
|
+
{{- '\n</tool_response>' }}
|
|
171
|
+
{%- if not loop.last and loop.nextitem.role != "tool" %}
|
|
172
|
+
{{- '<|im_end|>\n' }}
|
|
173
|
+
{%- elif loop.last %}
|
|
174
|
+
{{- '<|im_end|>\n' }}
|
|
175
|
+
{%- endif %}
|
|
176
|
+
{%- else %}
|
|
177
|
+
{{- raise_exception('Unexpected message role.') }}
|
|
178
|
+
{%- endif %}
|
|
179
|
+
{%- endfor %}
|
|
180
|
+
{%- if add_generation_prompt %}
|
|
181
|
+
{{- '<|im_start|>assistant\n' }}
|
|
182
|
+
{%- if ns_flags.enable_thinking is false %}
|
|
183
|
+
{{- '<think>\n\n</think>\n\n' }}
|
|
184
|
+
{%- else %}
|
|
185
|
+
{{- '<think>\n' }}
|
|
186
|
+
{%- endif %}
|
|
187
|
+
{%- endif %}
|