Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
A
ai-chat-xy
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
David Yang
ai-chat-xy
Commits
a3d4fa64
Commit
a3d4fa64
authored
Aug 27, 2026
by
David Yang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: route llm chat through backend
parent
1488c6e6
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
5 additions
and
120 deletions
+5
-120
.env.example
.env.example
+1
-6
.umirc.ts
.umirc.ts
+0
-7
README.md
README.md
+1
-13
DataService.js
src/services/DataService.js
+3
-94
No files found.
.env.example
View file @
a3d4fa64
UMI_APP_LLM_API_KEY=your_api_key_here
# 模型请求通过后端 /api/llm/chat 处理;不要在前端配置模型服务 URL 或 API Key。
# Optional overrides. Defaults are shown below.
# UMI_APP_LLM_API_BASE=/llm-api
# UMI_APP_LLM_CHAT_PATH=/v1/chat/completions
# UMI_APP_LLM_MODEL=gpt-5.5
.umirc.ts
View file @
a3d4fa64
...
@@ -64,13 +64,6 @@ export default defineConfig({
...
@@ -64,13 +64,6 @@ export default defineConfig({
changeOrigin
:
true
,
changeOrigin
:
true
,
pathRewrite
:
{
'^/api'
:
''
},
pathRewrite
:
{
'^/api'
:
''
},
},
},
'/llm-api'
:
{
target
:
'https://lingsuan.org'
,
changeOrigin
:
true
,
pathRewrite
:
{
'^/llm-api'
:
''
},
timeout
:
600000
,
proxyTimeout
:
600000
,
},
'/myapi/upload_invoices'
:
{
'/myapi/upload_invoices'
:
{
target
:
'http://1.94.111.115:5173'
,
target
:
'http://1.94.111.115:5173'
,
changeOrigin
:
true
,
changeOrigin
:
true
,
...
...
README.md
View file @
a3d4fa64
...
@@ -11,21 +11,9 @@
...
@@ -11,21 +11,9 @@
## 本地启动
## 本地启动
如果本地模型代理需要浏览器携带 API Key,先创建本地环境变量文件:
```
bash
cp
.env.example .env.local
```
然后把
`.env.local`
里的
`UMI_APP_LLM_API_KEY`
替换成线上模型服务 API Key。如果
`/llm-api`
代理已在服务端处理鉴权,可以不创建该文件。
```
bash
```
bash
pnpm
install
pnpm
install
pnpm dev
pnpm dev
```
```
可选配置:
聊天请求固定发送到同源后端接口
`/api/llm/chat`
。模型服务鉴权和上游地址均由后端管理,前端不需要也不应配置模型 API Key。
-
`UMI_APP_LLM_API_BASE`
:模型服务地址,默认走 Umi 代理
`/llm-api`
-
`UMI_APP_LLM_CHAT_PATH`
:聊天接口路径,默认
`/v1/chat/completions`
-
`UMI_APP_LLM_MODEL`
:默认模型,默认
`gpt-5.5`
src/services/DataService.js
View file @
a3d4fa64
...
@@ -15,29 +15,7 @@ function joinUrl(baseUrl, path) {
...
@@ -15,29 +15,7 @@ function joinUrl(baseUrl, path) {
return
`
${
baseUrl
.
replace
(
/
\/
+$/
,
''
)}
/
${
path
.
replace
(
/^
\/
+/
,
''
)}
`
;
return
`
${
baseUrl
.
replace
(
/
\/
+$/
,
''
)}
/
${
path
.
replace
(
/^
\/
+/
,
''
)}
`
;
}
}
const
DEFAULT_LLM_CHAT_PATH
=
'/v1/chat/completions'
;
const
BACKEND_LLM_CHAT_PATH
=
'/api/llm/chat'
;
const
SAME_ORIGIN_LLM_PROXY
=
'/llm-api'
;
const
PROXIED_LLM_HOSTS
=
new
Set
([
'edge.lingsuan.org'
,
'lingsuan.org'
]);
function
resolveConfiguredLlmUrl
(
url
,
path
)
{
if
(
!
url
)
return
null
;
if
(
path
)
return
joinUrl
(
url
,
path
);
if
(
/
\/(?:
chat
\/
completions|responses|completions
)\/?(?:[
?#
]
.*
)?
$/i
.
test
(
url
)
)
{
return
url
;
}
return
joinUrl
(
url
,
DEFAULT_LLM_CHAT_PATH
);
}
function
routeLlmUrlThroughSameOriginProxy
(
url
)
{
if
(
!
url
||
!
/^https
?
:
\/\/
/i
.
test
(
url
))
return
url
;
const
parsed
=
new
URL
(
url
);
if
(
!
PROXIED_LLM_HOSTS
.
has
(
parsed
.
hostname
.
toLowerCase
()))
return
url
;
return
`
${
SAME_ORIGIN_LLM_PROXY
}${
parsed
.
pathname
}${
parsed
.
search
}
`
;
}
function
firstDefined
(...
values
)
{
function
firstDefined
(...
values
)
{
return
values
.
find
((
value
)
=>
value
!==
undefined
&&
value
!==
null
);
return
values
.
find
((
value
)
=>
value
!==
undefined
&&
value
!==
null
);
...
@@ -276,31 +254,6 @@ function normalizeApplicationConfig(application = {}) {
...
@@ -276,31 +254,6 @@ function normalizeApplicationConfig(application = {}) {
settingsConfig
.
long_thinking_model
,
settingsConfig
.
long_thinking_model
,
),
),
},
},
apiKey
:
firstNonEmptyString
(
layoutsConfig
.
apiKey
,
layoutsConfig
.
api_key
,
layoutsConfig
.
apikey
,
layoutsConfig
.
llmApiKey
,
layoutsConfig
.
llm_api_key
,
llmConfig
.
apiKey
,
llmConfig
.
api_key
,
llmConfig
.
apikey
,
settingsConfig
.
apiKey
,
settingsConfig
.
api_key
,
settingsConfig
.
apikey
,
process
.
env
.
UMI_APP_LLM_API_KEY
,
process
.
env
.
VITE_LLM_API_KEY
,
),
authorization
:
firstNonEmptyString
(
layoutsConfig
.
authorization
,
llmConfig
.
authorization
,
settingsConfig
.
authorization
,
),
headers
:
firstDefined
(
layoutsConfig
.
headers
,
llmConfig
.
headers
,
settingsConfig
.
headers
,
),
requestParams
:
pickRequestParams
(
requestParams
:
pickRequestParams
(
settingsConfig
,
settingsConfig
,
llmConfig
,
llmConfig
,
...
@@ -384,31 +337,8 @@ function normalizeLlmList(payload) {
...
@@ -384,31 +337,8 @@ function normalizeLlmList(payload) {
return
[...
new
Map
(
models
.
map
((
model
)
=>
[
model
.
id
,
model
])).
values
()];
return
[...
new
Map
(
models
.
map
((
model
)
=>
[
model
.
id
,
model
])).
values
()];
}
}
function
resolveLlmUrl
(
llmConfig
=
{})
{
function
resolveLlmUrl
()
{
const
envBaseUrl
=
firstNonEmptyString
(
return
BACKEND_LLM_CHAT_PATH
;
process
.
env
.
UMI_APP_LLM_API_BASE
,
process
.
env
.
VITE_LLM_API_BASE
,
);
const
envPath
=
firstNonEmptyString
(
process
.
env
.
UMI_APP_LLM_CHAT_PATH
,
process
.
env
.
VITE_LLM_CHAT_PATH
,
);
const
configUrl
=
llmConfig
.
url
;
const
configPath
=
llmConfig
.
path
;
if
(
configUrl
)
{
return
routeLlmUrlThroughSameOriginProxy
(
resolveConfiguredLlmUrl
(
configUrl
,
configPath
),
);
}
if
(
envBaseUrl
)
{
return
routeLlmUrlThroughSameOriginProxy
(
resolveConfiguredLlmUrl
(
envBaseUrl
,
envPath
),
);
}
return
null
;
}
}
export
function
extractLlmChunkContent
(
value
)
{
export
function
extractLlmChunkContent
(
value
)
{
...
@@ -739,10 +669,6 @@ export async function llmChat(data, signal = undefined) {
...
@@ -739,10 +669,6 @@ export async function llmChat(data, signal = undefined) {
const
url
=
resolveLlmUrl
(
llmConfig
);
const
url
=
resolveLlmUrl
(
llmConfig
);
const
model
=
data
.
model
||
llmConfig
.
model
;
const
model
=
data
.
model
||
llmConfig
.
model
;
if
(
!
url
)
{
throw
new
Error
(
'应用配置缺少模型请求地址:请在 layouts.config.url 中配置'
);
}
if
(
!
model
)
{
if
(
!
model
)
{
throw
new
Error
(
'应用配置缺少模型名称:请在 layouts.config.model 中配置'
);
throw
new
Error
(
'应用配置缺少模型名称:请在 layouts.config.model 中配置'
);
}
}
...
@@ -752,23 +678,6 @@ export async function llmChat(data, signal = undefined) {
...
@@ -752,23 +678,6 @@ export async function llmChat(data, signal = undefined) {
'Content-Type'
:
'application/json'
,
'Content-Type'
:
'application/json'
,
});
});
const
configuredHeaders
=
normalizeObjectConfig
(
llmConfig
.
headers
);
if
(
Object
.
keys
(
configuredHeaders
).
length
)
{
Object
.
entries
(
configuredHeaders
).
forEach
(([
key
,
value
])
=>
{
if
(
value
!==
undefined
&&
value
!==
null
)
{
headers
.
set
(
key
,
String
(
value
));
}
});
}
if
(
llmConfig
.
authorization
&&
!
headers
.
has
(
'Authorization'
))
{
headers
.
set
(
'Authorization'
,
llmConfig
.
authorization
);
}
if
(
llmConfig
.
apiKey
&&
!
headers
.
has
(
'Authorization'
))
{
headers
.
set
(
'Authorization'
,
`Bearer
${
llmConfig
.
apiKey
}
`
);
}
const
response
=
await
fetch
(
url
,
{
const
response
=
await
fetch
(
url
,
{
method
:
'POST'
,
method
:
'POST'
,
headers
,
headers
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment