feat: add emulated tool-calling bridge for Lingma
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
This commit is contained in:
117
scripts/smoke_tool_calls.sh
Normal file
117
scripts/smoke_tool_calls.sh
Normal file
@@ -0,0 +1,117 @@
|
||||
#!/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'
|
||||
Reference in New Issue
Block a user