Add a proxy-side tool emulation layer so Lingma requests can surface stable OpenAI tool_calls and Anthropic tool_use blocks even when upstream tool events are missing or inconsistent. Constraint: Keep native Lingma tool event bridging as the first path and layer emulation as a fallback Rejected: Depend exclusively on Lingma native tool/invoke events | tool visibility remains inconsistent across models and transports Confidence: high Scope-risk: moderate
118 lines
3.9 KiB
Bash
118 lines
3.9 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
ENV_FILE="$ROOT_DIR/.env"
|
|
|
|
if [[ ! -f "$ENV_FILE" ]]; then
|
|
printf 'missing .env: %s\n' "$ENV_FILE" >&2
|
|
exit 1
|
|
fi
|
|
|
|
PORT="$(python3 - <<'PY'
|
|
from pathlib import Path
|
|
env = Path("/root/lingma-openai-gateway/.env")
|
|
vals = {}
|
|
for line in env.read_text().splitlines():
|
|
line = line.strip()
|
|
if not line or line.startswith('#') or '=' not in line:
|
|
continue
|
|
k, v = line.split('=', 1)
|
|
vals[k.strip()] = v.strip()
|
|
print(vals.get('PORT', '13013'))
|
|
PY
|
|
)"
|
|
|
|
API_KEY="$(python3 - <<'PY'
|
|
from pathlib import Path
|
|
env = Path("/root/lingma-openai-gateway/.env")
|
|
vals = {}
|
|
for line in env.read_text().splitlines():
|
|
line = line.strip()
|
|
if not line or line.startswith('#') or '=' not in line:
|
|
continue
|
|
k, v = line.split('=', 1)
|
|
vals[k.strip()] = v.strip()
|
|
keys = vals.get('API_KEYS', '')
|
|
print(keys.split(',')[0].strip())
|
|
PY
|
|
)"
|
|
|
|
BASE_URL="http://127.0.0.1:${PORT}"
|
|
|
|
printf '\n[1/5] /v1/models\n'
|
|
curl -fsS "$BASE_URL/v1/models" \
|
|
-H "Authorization: Bearer ${API_KEY}" | python3 -m json.tool
|
|
|
|
printf '\n[2/5] OpenAI non-stream tool call\n'
|
|
curl -fsS "$BASE_URL/v1/chat/completions" \
|
|
-H "Authorization: Bearer ${API_KEY}" \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{
|
|
"model": "org_auto",
|
|
"stream": false,
|
|
"messages": [
|
|
{"role": "system", "content": "Use tools when available."},
|
|
{"role": "user", "content": "Use fetch_weather for Hangzhou and return the tool call."}
|
|
],
|
|
"tools": [
|
|
{"type": "function", "function": {"name": "fetch_weather", "description": "Get weather for a city", "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}}
|
|
],
|
|
"tool_choice": {"type": "function", "function": {"name": "fetch_weather"}}
|
|
}' | python3 -m json.tool
|
|
|
|
printf '\n[3/5] Anthropic non-stream tool use\n'
|
|
curl -fsS "$BASE_URL/v1/messages" \
|
|
-H "x-api-key: ${API_KEY}" \
|
|
-H 'anthropic-version: 2023-06-01' \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{
|
|
"model": "claude-3-5-sonnet-20241022",
|
|
"max_tokens": 256,
|
|
"stream": false,
|
|
"messages": [
|
|
{"role": "user", "content": "Use fetch_weather for Hangzhou and return the tool call."}
|
|
],
|
|
"tools": [
|
|
{"name": "fetch_weather", "description": "Get weather for a city", "input_schema": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}
|
|
],
|
|
"tool_choice": {"type": "tool", "name": "fetch_weather"}
|
|
}' | python3 -m json.tool
|
|
|
|
printf '\n[4/5] OpenAI stream tool call\n'
|
|
curl -fsS -N "$BASE_URL/v1/chat/completions" \
|
|
-H "Authorization: Bearer ${API_KEY}" \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{
|
|
"model": "org_auto",
|
|
"stream": true,
|
|
"messages": [
|
|
{"role": "system", "content": "Use tools when available."},
|
|
{"role": "user", "content": "Use fetch_weather for Hangzhou and return the tool call."}
|
|
],
|
|
"tools": [
|
|
{"type": "function", "function": {"name": "fetch_weather", "description": "Get weather for a city", "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}}
|
|
],
|
|
"tool_choice": {"type": "function", "function": {"name": "fetch_weather"}}
|
|
}'
|
|
|
|
printf '\n[5/5] Anthropic stream tool use\n'
|
|
curl -fsS -N "$BASE_URL/v1/messages" \
|
|
-H "x-api-key: ${API_KEY}" \
|
|
-H 'anthropic-version: 2023-06-01' \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{
|
|
"model": "claude-3-5-sonnet-20241022",
|
|
"max_tokens": 256,
|
|
"stream": true,
|
|
"messages": [
|
|
{"role": "user", "content": "Use fetch_weather for Hangzhou and return the tool call."}
|
|
],
|
|
"tools": [
|
|
{"name": "fetch_weather", "description": "Get weather for a city", "input_schema": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}
|
|
],
|
|
"tool_choice": {"type": "tool", "name": "fetch_weather"}
|
|
}'
|
|
|
|
printf '\nsmoke tool-call checks completed\n'
|