diff --git a/pipeline_llm/inference.py b/pipeline_llm/inference.py index 9a44a02..1470738 100644 --- a/pipeline_llm/inference.py +++ b/pipeline_llm/inference.py @@ -298,6 +298,23 @@ async def _render_headers(tmpl, ns): return out +def _join_url(api_base, path): + """api_base + path 拼接(2026-09-05 404 教训):模板 path 若把 base_url + 已有的路径前缀带上(提取 LLM 给 /api/v1/tasks/...,端点又是 .../api/v1), + 朴素字符串相加会双前缀 404——重叠段去重后再拼。""" + base = (api_base or '').rstrip('/') + p = (path or '').strip() + if not base: + return p + if not p.startswith('/'): + p = '/' + p + from urllib.parse import urlparse + bp = urlparse(base).path.rstrip('/') + if bp and p.startswith(bp + '/'): + p = p[len(bp):] + return base + p + + async def _build_upstream_body(ctx, payload): """请求体模板渲染(uapi data 模板同款)。 @@ -348,7 +365,7 @@ async def _call_upstream_chat(ctx, payload): if path.startswith('http://') or path.startswith('https://'): url = path else: - url = (ctx.get('api_base') or '').rstrip('/') + path + url = _join_url(ctx.get('api_base'), path) headers = await _render_headers(profile.get('headers', ''), ns) body = await _build_upstream_body(ctx, payload) timeout = int(_fnum(ctx.get('timeout')) or _TOTAL_TIMEOUT) @@ -500,7 +517,7 @@ async def _async_query_once(ctx, qprofile, task_id, ns, timeout): if path.startswith('http://') or path.startswith('https://'): url = path else: - url = (ctx.get('api_base') or '').rstrip('/') + path + url = _join_url(ctx.get('api_base'), path) headers = await _render_headers(qprofile.get('headers', ''), ns) method = (qprofile.get('method') or 'GET').upper() status, data = await _http_request(method, url, headers, None, timeout) @@ -541,7 +558,7 @@ async def _async_inference(ctx, payload, req_timeout): if path.startswith('http://') or path.startswith('https://'): url = path else: - url = (ctx.get('api_base') or '').rstrip('/') + path + url = _join_url(ctx.get('api_base'), path) headers = await _render_headers(profile.get('headers', ''), ns) try: status, data = await _http_request('POST', url, headers, body, step_timeout)