llm,doc

This commit is contained in:
yvan 2025-08-02 23:54:03 +08:00
parent 366ca479c4
commit e03259608e
17 changed files with 3836 additions and 212 deletions

View File

@ -10,7 +10,7 @@ autocode:
root: /Users/yvan/GolandProjects/yvan/pet-ai
server: server
module: github.com/flipped-aurora/gin-vue-admin/server
ai-path: ""
ai-path: "https://ai.gin-vue-admin.com/{FUNC}/xyvan329/fb12d6eb-4e21-4ed3-8fb3-9013d57ccf44"
aws-s3:
bucket: xxxxx-10005608
region: ap-shanghai
@ -236,6 +236,12 @@ tencent-cos:
secret-key: your-secret-key
base-url: https://gin.vue.admin
path-prefix: github.com/flipped-aurora/gin-vue-admin/server
volcengine:
access-key: "your-access-key"
secret-key: "your-secret-key"
region: "cn-beijing"
endpoint: "https://ark.cn-beijing.volces.com"
default-model: "ep-xxx"
zap:
level: info
prefix: '[github.com/flipped-aurora/gin-vue-admin/server]'

View File

@ -37,4 +37,7 @@ type Server struct {
// MCP配置
MCP MCP `mapstructure:"mcp" json:"mcp" yaml:"mcp"`
// Volcengine配置
Volcengine Volcengine `mapstructure:"volcengine" json:"volcengine" yaml:"volcengine"`
}

View File

@ -0,0 +1,9 @@
package config
type Volcengine struct {
AccessKey string `mapstructure:"access-key" json:"access-key" yaml:"access-key"` // 火山引擎访问密钥ID
SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"` // 火山引擎访问密钥Secret
Region string `mapstructure:"region" json:"region" yaml:"region"` // 区域cn-beijing
Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"` // 服务端点https://ark.cn-beijing.volces.com
DefaultModel string `mapstructure:"default-model" json:"default-model" yaml:"default-model"` // 默认模型IDep-xxx
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -85,7 +85,7 @@ definitions:
description: 验证码长度
type: integer
open-captcha:
description: 防爆破验证码开启此数0代表每次登录都需要验证码其他数字代表错误密码如3代表错误三次后出现验证码
description: 防爆破验证码开启此数0代表每次登录都需要验证码其他数字代表错误密码如3代表错误三次后出现验证码
type: integer
open-captcha-timeout:
description: 防爆破验证码超时时间单位s(秒)
@ -153,6 +153,24 @@ definitions:
description: 本地文件存储路径
type: string
type: object
config.MCP:
properties:
message_path:
description: 消息路径
type: string
name:
description: MCP名称
type: string
sse_path:
description: SSE路径
type: string
url_prefix:
description: URL前缀
type: string
version:
description: MCP版本
type: string
type: object
config.Minio:
properties:
access-key-id:
@ -477,6 +495,10 @@ definitions:
allOf:
- $ref: '#/definitions/config.Local'
description: oss
mcp:
allOf:
- $ref: '#/definitions/config.MCP'
description: MCP配置
minio:
$ref: '#/definitions/config.Minio'
mongo:
@ -505,6 +527,10 @@ definitions:
$ref: '#/definitions/config.System'
tencent-cos:
$ref: '#/definitions/config.TencentCOS'
volcengine:
allOf:
- $ref: '#/definitions/config.Volcengine'
description: Volcengine配置
zap:
$ref: '#/definitions/config.Zap'
type: object
@ -645,6 +671,24 @@ definitions:
secret-key:
type: string
type: object
config.Volcengine:
properties:
access-key:
description: 火山引擎访问密钥ID
type: string
default-model:
description: 默认模型IDep-xxx
type: string
endpoint:
description: 服务端点https://ark.cn-beijing.volces.com
type: string
region:
description: 区域cn-beijing
type: string
secret-key:
description: 火山引擎访问密钥Secret
type: string
type: object
config.Zap:
properties:
director:
@ -802,6 +846,9 @@ definitions:
host:
description: 服务器地址 例如 smtp.qq.com 请前往QQ或者你要发邮件的邮箱查看其smtp协议
type: string
is-loginauth:
description: 是否LoginAuth 是否使用LoginAuth认证方式适用于IBM、微软邮箱服务器等
type: boolean
is-ssl:
description: 是否SSL 是否开启SSL
type: boolean
@ -1014,6 +1061,46 @@ definitions:
description: 是否前端表格列
type: boolean
type: object
request.AutoMcpTool:
properties:
description:
type: string
name:
type: string
params:
items:
properties:
default:
type: string
description:
type: string
name:
type: string
required:
type: boolean
type:
description: string, number, boolean, object, array
type: string
required:
- description
- name
- type
type: object
type: array
response:
items:
properties:
type:
description: text, image
type: string
required:
- type
type: object
type: array
required:
- description
- name
type: object
request.CasbinInReceive:
properties:
authorityId:
@ -1042,6 +1129,34 @@ definitions:
description: 密码
type: string
type: object
request.ChatRequest:
properties:
max_tokens:
description: 最大生成token数
type: integer
messages:
description: 对话消息列表
items:
$ref: '#/definitions/request.Message'
type: array
model:
description: 模型名称,如果为空则使用默认模型
type: string
request_id:
description: 请求ID用于流式响应管理
type: string
stream:
description: 是否流式响应默认false
type: boolean
temperature:
description: 温度参数控制随机性范围0-1
type: number
top_p:
description: 核采样参数
type: number
required:
- messages
type: object
request.DataSource:
properties:
association:
@ -1074,6 +1189,31 @@ definitions:
description: 每页大小
type: integer
type: object
request.ExportVersionRequest:
properties:
apiIds:
description: 选中的API ID列表
items:
type: integer
type: array
description:
description: 版本描述
type: string
menuIds:
description: 选中的菜单ID列表
items:
type: integer
type: array
versionCode:
description: 版本号
type: string
versionName:
description: 版本名称
type: string
required:
- versionCode
- versionName
type: object
request.GetAuthorityId:
properties:
authorityId:
@ -1113,6 +1253,25 @@ definitions:
type: integer
type: array
type: object
request.ImportVersionRequest:
properties:
apis:
description: API数据直接复用SysApi
items:
$ref: '#/definitions/system.SysApi'
type: array
menus:
description: 菜单数据直接复用SysBaseMenu
items:
$ref: '#/definitions/system.SysBaseMenu'
type: array
version:
allOf:
- $ref: '#/definitions/request.VersionInfo'
description: 版本信息
required:
- version
type: object
request.InitDB:
properties:
adminPassword:
@ -1160,6 +1319,18 @@ definitions:
description: 用户名
type: string
type: object
request.Message:
properties:
content:
description: 消息内容
type: string
role:
description: '角色: system, user, assistant'
type: string
required:
- content
- role
type: object
request.PageInfo:
properties:
keyword:
@ -1257,6 +1428,14 @@ definitions:
id:
type: integer
type: object
request.StopRequest:
properties:
request_id:
description: 要停止的请求ID
type: string
required:
- request_id
type: object
request.SysAuthorityBtnReq:
properties:
authorityId:
@ -1298,6 +1477,81 @@ definitions:
description: 主键ID
type: integer
type: object
request.VersionInfo:
properties:
code:
description: 版本号
type: string
description:
description: 版本描述
type: string
exportTime:
description: 导出时间
type: string
name:
description: 版本名称
type: string
required:
- code
- name
type: object
response.APIError:
properties:
code:
description: 错误代码
type: string
message:
description: 错误消息
type: string
type:
description: 错误类型
type: string
type: object
response.ChatResponse:
properties:
choices:
description: 响应选择列表
items:
$ref: '#/definitions/response.Choice'
type: array
created:
description: 创建时间戳
type: integer
error:
allOf:
- $ref: '#/definitions/response.APIError'
description: 错误信息
id:
description: 响应ID
type: string
model:
description: 使用的模型
type: string
object:
description: '对象类型: chat.completion 或 chat.completion.chunk'
type: string
usage:
allOf:
- $ref: '#/definitions/response.Usage'
description: 使用量统计(非流式响应)
type: object
response.Choice:
properties:
delta:
allOf:
- $ref: '#/definitions/response.Message'
description: 增量消息(流式)
finish_reason:
description: '结束原因: stop, length, content_filter'
type: string
index:
description: 选择索引
type: integer
message:
allOf:
- $ref: '#/definitions/response.Message'
description: 完整响应消息(非流式)
type: object
response.Email:
properties:
body:
@ -1339,6 +1593,15 @@ definitions:
user:
$ref: '#/definitions/system.SysUser'
type: object
response.Message:
properties:
content:
description: 消息内容
type: string
role:
description: '角色: system, user, assistant'
type: string
type: object
response.PageResult:
properties:
list: {}
@ -1364,6 +1627,21 @@ definitions:
msg:
type: string
type: object
response.StopResponse:
properties:
message:
description: 响应消息
type: string
request_id:
description: 请求ID
type: string
stopped_at:
description: 停止时间戳
type: integer
success:
description: 是否成功停止
type: boolean
type: object
response.SysAPIListResponse:
properties:
apis:
@ -1436,6 +1714,18 @@ definitions:
user:
$ref: '#/definitions/system.SysUser'
type: object
response.Usage:
properties:
completion_tokens:
description: 输出token数
type: integer
prompt_tokens:
description: 输入token数
type: integer
total_tokens:
description: 总token数
type: integer
type: object
system.Condition:
properties:
ID:
@ -1495,6 +1785,9 @@ definitions:
title:
description: 菜单名
type: string
transitionType:
description: 路由切换动画
type: string
type: object
system.SysApi:
properties:
@ -1920,6 +2213,33 @@ definitions:
description: 用户UUID
type: string
type: object
system.SysVersion:
properties:
ID:
description: 主键ID
type: integer
createdAt:
description: 创建时间
type: string
description:
description: 版本描述
type: string
updatedAt:
description: 更新时间
type: string
versionCode:
description: 版本号
type: string
versionData:
description: 版本数据
type: string
versionName:
description: 版本名称
type: string
required:
- versionCode
- versionName
type: object
system.System:
properties:
config:
@ -1929,7 +2249,7 @@ info:
contact: {}
description: 使用gin+vue进行极速开发的全栈开发基础平台
title: Gin-Vue-Admin Swagger API接口文档
version: v2.7.9-beta
version: v2.8.4
paths:
/api/createApi:
post:
@ -2935,8 +3255,7 @@ paths:
- $ref: '#/definitions/response.Response'
- properties:
data:
items:
type: object
items: {}
type: array
msg:
type: string
@ -2946,6 +3265,75 @@ paths:
summary: 安装插件
tags:
- AutoCodePlugin
/autoCode/mcp:
post:
consumes:
- application/json
parameters:
- description: 创建自动代码
in: body
name: data
required: true
schema:
$ref: '#/definitions/request.AutoMcpTool'
produces:
- application/json
responses:
"200":
description: '{"success":true,"data":{},"msg":"创建成功"}'
schema:
type: string
security:
- ApiKeyAuth: []
summary: 自动McpTool
tags:
- mcp
/autoCode/mcpList:
post:
consumes:
- application/json
parameters:
- description: 创建自动代码
in: body
name: data
required: true
schema:
$ref: '#/definitions/request.AutoMcpTool'
produces:
- application/json
responses:
"200":
description: '{"success":true,"data":{},"msg":"创建成功"}'
schema:
type: string
security:
- ApiKeyAuth: []
summary: 自动McpTool
tags:
- mcp
/autoCode/mcpTest:
post:
consumes:
- application/json
parameters:
- description: 调用MCP Tool的参数
in: body
name: data
required: true
schema:
type: object
produces:
- application/json
responses:
"200":
description: '{"success":true,"data":{},"msg":"测试成功"}'
schema:
$ref: '#/definitions/response.Response'
security:
- ApiKeyAuth: []
summary: 测试McpTool
tags:
- mcp
/autoCode/preview:
post:
consumes:
@ -4525,18 +4913,6 @@ paths:
summary: 更新SysDictionaryDetail
tags:
- SysDictionaryDetail
/sysExportTemplate/ExportTemplate:
get:
consumes:
- application/json
produces:
- application/json
responses: {}
security:
- ApiKeyAuth: []
summary: 导出表格模板
tags:
- SysExportTemplate
/sysExportTemplate/createSysExportTemplate:
post:
consumes:
@ -4618,6 +4994,42 @@ paths:
summary: 导出表格
tags:
- SysExportTemplate
/sysExportTemplate/exportExcelByToken:
get:
consumes:
- application/json
produces:
- application/json
responses: {}
security:
- ApiKeyAuth: []
summary: 导出表格
tags:
- ExportExcelByToken
/sysExportTemplate/exportTemplate:
get:
consumes:
- application/json
produces:
- application/json
responses: {}
security:
- ApiKeyAuth: []
summary: 导出表格模板
tags:
- SysExportTemplate
/sysExportTemplate/exportTemplateByToken:
get:
consumes:
- application/json
produces:
- application/json
responses: {}
security:
- ApiKeyAuth: []
summary: 通过token导出表格模板
tags:
- ExportTemplateByToken
/sysExportTemplate/findSysExportTemplate:
get:
consumes:
@ -4781,34 +5193,6 @@ paths:
summary: 更新导出模板
tags:
- SysExportTemplate
/sysOperationRecord/createSysOperationRecord:
post:
consumes:
- application/json
parameters:
- description: 创建SysOperationRecord
in: body
name: data
required: true
schema:
$ref: '#/definitions/system.SysOperationRecord'
produces:
- application/json
responses:
"200":
description: 创建SysOperationRecord
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 创建SysOperationRecord
tags:
- SysOperationRecord
/sysOperationRecord/deleteSysOperationRecord:
delete:
consumes:
@ -5267,6 +5651,238 @@ paths:
summary: 更新参数
tags:
- SysParams
/sysVersion/deleteSysVersion:
delete:
consumes:
- application/json
parameters:
- description: 删除版本管理
in: body
name: data
required: true
schema:
$ref: '#/definitions/system.SysVersion'
produces:
- application/json
responses:
"200":
description: 删除成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 删除版本管理
tags:
- SysVersion
/sysVersion/deleteSysVersionByIds:
delete:
consumes:
- application/json
produces:
- application/json
responses:
"200":
description: 批量删除成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 批量删除版本管理
tags:
- SysVersion
/sysVersion/downloadVersionJson:
get:
consumes:
- application/json
parameters:
- description: 版本ID
in: query
name: ID
required: true
type: string
produces:
- application/json
responses:
"200":
description: 下载成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
type: object
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 下载版本JSON数据
tags:
- SysVersion
/sysVersion/exportVersion:
post:
consumes:
- application/json
parameters:
- description: 创建发版数据
in: body
name: data
required: true
schema:
$ref: '#/definitions/request.ExportVersionRequest'
produces:
- application/json
responses:
"200":
description: 创建成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 创建发版数据
tags:
- SysVersion
/sysVersion/findSysVersion:
get:
consumes:
- application/json
parameters:
- description: 用id查询版本管理
in: query
name: ID
required: true
type: integer
produces:
- application/json
responses:
"200":
description: 查询成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/system.SysVersion'
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 用id查询版本管理
tags:
- SysVersion
/sysVersion/getSysVersionList:
get:
consumes:
- application/json
parameters:
- collectionFormat: csv
in: query
items:
type: string
name: createdAtRange
type: array
- description: 关键字
in: query
name: keyword
type: string
- description: 页码
in: query
name: page
type: integer
- description: 每页大小
in: query
name: pageSize
type: integer
- in: query
name: versionCode
type: string
- in: query
name: versionName
type: string
produces:
- application/json
responses:
"200":
description: 获取成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/response.PageResult'
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 分页获取版本管理列表
tags:
- SysVersion
/sysVersion/getSysVersionPublic:
get:
consumes:
- application/json
produces:
- application/json
responses:
"200":
description: 获取成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
type: object
msg:
type: string
type: object
summary: 不需要鉴权的版本管理接口
tags:
- SysVersion
/sysVersion/importVersion:
post:
consumes:
- application/json
parameters:
- description: 版本JSON数据
in: body
name: data
required: true
schema:
$ref: '#/definitions/request.ImportVersionRequest'
produces:
- application/json
responses:
"200":
description: 导入成功
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
msg:
type: string
type: object
security:
- ApiKeyAuth: []
summary: 导入版本数据
tags:
- SysVersion
/system/getServerInfo:
post:
produces:
@ -5316,7 +5932,7 @@ paths:
- application/json
responses:
"200":
description: 系统
description: 系统
schema:
allOf:
- $ref: '#/definitions/response.Response'
@ -5326,7 +5942,7 @@ paths:
type: object
security:
- ApiKeyAuth: []
summary: 系统
summary: 系统
tags:
- System
/system/setSystemConfig:
@ -5665,6 +6281,81 @@ paths:
summary: 设置用户信息
tags:
- SysUser
/volcengine/llm/chat:
post:
consumes:
- application/json
parameters:
- description: 聊天请求参数
in: body
name: data
required: true
schema:
$ref: '#/definitions/request.ChatRequest'
produces:
- application/json
responses:
"200":
description: 聊天响应
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/response.ChatResponse'
type: object
security:
- ApiKeyAuth: []
summary: LLM聊天完成
tags:
- Volcengine
/volcengine/llm/sessions:
get:
produces:
- application/json
responses:
"200":
description: 活跃会话数量
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
type: integer
type: object
security:
- ApiKeyAuth: []
summary: 获取活跃会话数量
tags:
- Volcengine
/volcengine/llm/stop:
post:
consumes:
- application/json
parameters:
- description: 停止请求参数
in: body
name: data
required: true
schema:
$ref: '#/definitions/request.StopRequest'
produces:
- application/json
responses:
"200":
description: 停止响应
schema:
allOf:
- $ref: '#/definitions/response.Response'
- properties:
data:
$ref: '#/definitions/response.StopResponse'
type: object
security:
- ApiKeyAuth: []
summary: 停止LLM生成
tags:
- Volcengine
securityDefinitions:
ApiKeyAuth:
in: header

View File

@ -149,6 +149,8 @@ require (
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect
github.com/volcengine/volc-sdk-golang v1.0.23 // indirect
github.com/volcengine/volcengine-go-sdk v1.1.25 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
@ -170,6 +172,7 @@ require (
golang.org/x/tools v0.29.0 // indirect
google.golang.org/protobuf v1.36.3 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/hints v1.1.2 // indirect
gorm.io/plugin/dbresolver v1.5.3 // indirect

View File

@ -54,6 +54,7 @@ github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk=
github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
@ -190,6 +191,13 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@ -198,6 +206,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
@ -288,6 +297,7 @@ github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@ -429,6 +439,7 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@ -464,6 +475,10 @@ github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/unrolled/secure v1.17.0 h1:Io7ifFgo99Bnh0J7+Q+qcMzWM6kaDPCA5FroFZEdbWU=
github.com/unrolled/secure v1.17.0/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
github.com/volcengine/volc-sdk-golang v1.0.23 h1:anOslb2Qp6ywnsbyq9jqR0ljuO63kg9PY+4OehIk5R8=
github.com/volcengine/volc-sdk-golang v1.0.23/go.mod h1:AfG/PZRUkHJ9inETvbjNifTDgut25Wbkm2QoYBTbvyU=
github.com/volcengine/volcengine-go-sdk v1.1.25 h1:wwR2DTJGw2sOZ1wTWaQLn03PGO0O+motGvsoVvAp5Zk=
github.com/volcengine/volcengine-go-sdk v1.1.25/go.mod h1:EyKoi6t6eZxoPNGr2GdFCZti2Skd7MO3eUzx7TtSvNo=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
@ -744,6 +759,7 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@ -751,10 +767,20 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=
google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=

View File

@ -5,6 +5,7 @@ import (
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/email"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/volcengine"
"github.com/flipped-aurora/gin-vue-admin/server/utils/plugin"
"github.com/gin-gonic/gin"
)
@ -22,15 +23,18 @@ func bizPluginV1(group ...*gin.RouterGroup) {
private := group[0]
public := group[1]
// 添加跟角色挂钩权限的插件 示例 本地示例模式于在线仓库模式注意上方的import 可以自行切换 效果相同
PluginInit(private, email.CreateEmailPlug(
global.GVA_CONFIG.Email.To,
global.GVA_CONFIG.Email.From,
global.GVA_CONFIG.Email.Host,
global.GVA_CONFIG.Email.Secret,
global.GVA_CONFIG.Email.Nickname,
global.GVA_CONFIG.Email.Port,
global.GVA_CONFIG.Email.IsSSL,
global.GVA_CONFIG.Email.IsLoginAuth,
))
PluginInit(private,
email.CreateEmailPlug(
global.GVA_CONFIG.Email.To,
global.GVA_CONFIG.Email.From,
global.GVA_CONFIG.Email.Host,
global.GVA_CONFIG.Email.Secret,
global.GVA_CONFIG.Email.Nickname,
global.GVA_CONFIG.Email.Port,
global.GVA_CONFIG.Email.IsSSL,
global.GVA_CONFIG.Email.IsLoginAuth,
),
volcengine.CreateVolcenginePlug(),
)
holder(public, private)
}

View File

@ -0,0 +1,7 @@
package api
type ApiGroup struct {
LLMApi
}
var ApiGroupApp = new(ApiGroup)

View File

@ -0,0 +1,177 @@
package api
import (
"fmt"
"io"
"time"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/volcengine/model/request"
volcengineResponse "github.com/flipped-aurora/gin-vue-admin/server/plugin/volcengine/model/response"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/volcengine/service"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"go.uber.org/zap"
)
type LLMApi struct{}
// ChatCompletion
// @Tags Volcengine
// @Summary LLM聊天完成
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.ChatRequest true "聊天请求参数"
// @Success 200 {object} response.Response{data=volcengineResponse.ChatResponse} "聊天响应"
// @Router /volcengine/llm/chat [post]
func (l *LLMApi) ChatCompletion(c *gin.Context) {
var chatReq request.ChatRequest
err := c.ShouldBindJSON(&chatReq)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
// 生成请求ID
if chatReq.RequestID == "" {
chatReq.RequestID = uuid.New().String()
}
// 根据stream参数选择响应模式
if chatReq.Stream {
// 流式响应 (SSE)
l.handleStreamResponse(c, chatReq)
} else {
// 普通JSON响应
l.handleNormalResponse(c, chatReq)
}
}
// handleNormalResponse 处理普通JSON响应
func (l *LLMApi) handleNormalResponse(c *gin.Context, chatReq request.ChatRequest) {
resp, err := service.LLMServiceApp.ChatCompletion(chatReq)
if err != nil {
global.GVA_LOG.Error("聊天完成失败!", zap.Error(err), zap.String("requestID", chatReq.RequestID))
response.FailWithMessage("聊天完成失败: "+err.Error(), c)
return
}
global.GVA_LOG.Info("聊天完成成功", zap.String("requestID", chatReq.RequestID))
response.OkWithDetailed(resp, "聊天完成成功", c)
}
// handleStreamResponse 处理流式响应 (SSE)
func (l *LLMApi) handleStreamResponse(c *gin.Context, chatReq request.ChatRequest) {
// 设置SSE响应头
c.Header("Content-Type", "text/event-stream")
c.Header("Cache-Control", "no-cache")
c.Header("Connection", "keep-alive")
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Headers", "Cache-Control")
// 创建事件通道
eventChan := make(chan volcengineResponse.StreamEvent, 100)
defer close(eventChan)
// 启动流式处理
go func() {
err := service.LLMServiceApp.StreamChatCompletion(chatReq, eventChan)
if err != nil {
global.GVA_LOG.Error("流式聊天失败!", zap.Error(err), zap.String("requestID", chatReq.RequestID))
eventChan <- volcengineResponse.StreamEvent{
Event: "error",
Data: volcengineResponse.ChatResponse{
ID: chatReq.RequestID,
Error: &volcengineResponse.APIError{
Code: "stream_error",
Message: err.Error(),
Type: "internal_error",
},
},
}
}
}()
// 发送流式数据
c.Stream(func(w io.Writer) bool {
select {
case event, ok := <-eventChan:
if !ok {
return false
}
switch event.Event {
case "message":
// 发送消息事件
c.SSEvent("message", event.Data)
case "error":
// 发送错误事件
c.SSEvent("error", event.Data)
return false
case "done":
// 发送完成事件
c.SSEvent("done", event.Data)
return false
}
return true
case <-time.After(30 * time.Second):
// 超时处理
c.SSEvent("error", volcengineResponse.ChatResponse{
ID: chatReq.RequestID,
Error: &volcengineResponse.APIError{
Code: "timeout",
Message: "Stream timeout",
Type: "timeout_error",
},
})
return false
}
})
}
// StopGeneration
// @Tags Volcengine
// @Summary 停止LLM生成
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.StopRequest true "停止请求参数"
// @Success 200 {object} response.Response{data=volcengineResponse.StopResponse} "停止响应"
// @Router /volcengine/llm/stop [post]
func (l *LLMApi) StopGeneration(c *gin.Context) {
var stopReq request.StopRequest
err := c.ShouldBindJSON(&stopReq)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
resp, err := service.LLMServiceApp.StopGeneration(stopReq)
if err != nil {
global.GVA_LOG.Error("停止生成失败!", zap.Error(err), zap.String("requestID", stopReq.RequestID))
response.FailWithMessage("停止生成失败: "+err.Error(), c)
return
}
if resp.Success {
global.GVA_LOG.Info("停止生成成功", zap.String("requestID", stopReq.RequestID))
response.OkWithDetailed(resp, "停止生成成功", c)
} else {
global.GVA_LOG.Warn("停止生成失败", zap.String("requestID", stopReq.RequestID), zap.String("reason", resp.Message))
response.FailWithDetailed(resp, resp.Message, c)
}
}
// GetActiveSessionsCount
// @Tags Volcengine
// @Summary 获取活跃会话数量
// @Security ApiKeyAuth
// @Produce application/json
// @Success 200 {object} response.Response{data=int} "活跃会话数量"
// @Router /volcengine/llm/sessions [get]
func (l *LLMApi) GetActiveSessionsCount(c *gin.Context) {
count := service.LLMServiceApp.GetActiveSessionsCount()
response.OkWithDetailed(gin.H{"count": count}, fmt.Sprintf("当前活跃会话数量: %d", count), c)
}

View File

@ -0,0 +1,23 @@
package volcengine
import (
"github.com/flipped-aurora/gin-vue-admin/server/plugin/volcengine/router"
"github.com/gin-gonic/gin"
)
type volcenginePlugin struct{}
// CreateVolcenginePlug 创建volcengine插件实例
func CreateVolcenginePlug() *volcenginePlugin {
return &volcenginePlugin{}
}
// Register 注册路由
func (*volcenginePlugin) Register(group *gin.RouterGroup) {
router.RouterGroupApp.InitLLMRouter(group)
}
// RouterPath 返回注册路由路径
func (*volcenginePlugin) RouterPath() string {
return "volcengine"
}

View File

@ -0,0 +1,23 @@
package request
// Message 聊天消息结构体
type Message struct {
Role string `json:"role" form:"role" binding:"required"` // 角色: system, user, assistant
Content string `json:"content" form:"content" binding:"required"` // 消息内容
}
// ChatRequest 聊天请求结构体
type ChatRequest struct {
Messages []Message `json:"messages" form:"messages" binding:"required"` // 对话消息列表
Model string `json:"model" form:"model"` // 模型名称,如果为空则使用默认模型
Stream bool `json:"stream" form:"stream"` // 是否流式响应默认false
Temperature float64 `json:"temperature" form:"temperature"` // 温度参数控制随机性范围0-1
MaxTokens int `json:"max_tokens" form:"max_tokens"` // 最大生成token数
TopP float64 `json:"top_p" form:"top_p"` // 核采样参数
RequestID string `json:"request_id,omitempty" form:"request_id,omitempty"` // 请求ID用于流式响应管理
}
// StopRequest 停止生成请求结构体
type StopRequest struct {
RequestID string `json:"request_id" form:"request_id" binding:"required"` // 要停止的请求ID
}

View File

@ -0,0 +1,66 @@
package response
import "time"
// Message 聊天消息结构体
type Message struct {
Role string `json:"role"` // 角色: system, user, assistant
Content string `json:"content"` // 消息内容
}
// Choice 响应选择结构体
type Choice struct {
Index int `json:"index"` // 选择索引
Message Message `json:"message,omitempty"` // 完整响应消息(非流式)
Delta Message `json:"delta,omitempty"` // 增量消息(流式)
FinishReason string `json:"finish_reason,omitempty"` // 结束原因: stop, length, content_filter
}
// Usage 使用量统计结构体
type Usage struct {
PromptTokens int `json:"prompt_tokens"` // 输入token数
CompletionTokens int `json:"completion_tokens"` // 输出token数
TotalTokens int `json:"total_tokens"` // 总token数
}
// APIError API错误结构体
type APIError struct {
Code string `json:"code"` // 错误代码
Message string `json:"message"` // 错误消息
Type string `json:"type"` // 错误类型
}
// ChatResponse 聊天响应结构体
type ChatResponse struct {
ID string `json:"id"` // 响应ID
Object string `json:"object"` // 对象类型: chat.completion 或 chat.completion.chunk
Created int64 `json:"created"` // 创建时间戳
Model string `json:"model"` // 使用的模型
Choices []Choice `json:"choices"` // 响应选择列表
Usage *Usage `json:"usage,omitempty"` // 使用量统计(非流式响应)
Error *APIError `json:"error,omitempty"` // 错误信息
}
// StopResponse 停止生成响应结构体
type StopResponse struct {
Success bool `json:"success"` // 是否成功停止
Message string `json:"message"` // 响应消息
RequestID string `json:"request_id"` // 请求ID
StoppedAt int64 `json:"stopped_at"` // 停止时间戳
}
// StreamEvent 流式事件结构体
type StreamEvent struct {
Event string `json:"event"` // 事件类型: message, error, done
Data ChatResponse `json:"data"` // 事件数据
}
// ChatSession 聊天会话管理结构体(用于内部管理)
type ChatSession struct {
RequestID string `json:"request_id"` // 请求ID
UserID uint `json:"user_id"` // 用户ID
Model string `json:"model"` // 使用的模型
StartTime time.Time `json:"start_time"` // 开始时间
IsStreaming bool `json:"is_streaming"` // 是否流式响应
Status string `json:"status"` // 状态: running, completed, stopped, error
}

View File

@ -0,0 +1,7 @@
package router
type RouterGroup struct {
LLMRouter
}
var RouterGroupApp = new(RouterGroup)

View File

@ -0,0 +1,30 @@
package router
import (
"github.com/flipped-aurora/gin-vue-admin/server/middleware"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/volcengine/api"
"github.com/gin-gonic/gin"
)
type LLMRouter struct{}
// InitLLMRouter 初始化LLM路由
func (l *LLMRouter) InitLLMRouter(Router *gin.RouterGroup) {
// LLM相关路由组使用操作记录中间件
llmRouter := Router.Group("llm").Use(middleware.OperationRecord())
// LLM查询路由组不使用操作记录中间件用于GET请求
llmRouterWithoutRecord := Router.Group("llm")
// 获取API实例
llmApi := api.ApiGroupApp.LLMApi
{
// 需要记录操作的路由POST请求
llmRouter.POST("chat", llmApi.ChatCompletion) // LLM聊天完成
llmRouter.POST("stop", llmApi.StopGeneration) // 停止LLM生成
}
{
// 不需要记录操作的路由GET请求
llmRouterWithoutRecord.GET("sessions", llmApi.GetActiveSessionsCount) // 获取活跃会话数量
}
}

View File

@ -0,0 +1,316 @@
package service
import (
"context"
"fmt"
"sync"
"time"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/volcengine/model/request"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/volcengine/model/response"
"github.com/google/uuid"
"github.com/volcengine/volcengine-go-sdk/service/arkruntime"
"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
"go.uber.org/zap"
)
type LLMService struct {
client *arkruntime.Client
activeSessions sync.Map // requestID -> context.CancelFunc
}
var LLMServiceApp = new(LLMService)
// InitVolcengineClient 初始化火山引擎客户端
func (l *LLMService) InitVolcengineClient() error {
config := global.GVA_CONFIG.Volcengine
// 验证配置
if config.AccessKey == "" || config.SecretKey == "" {
return fmt.Errorf("volcengine access key or secret key is empty")
}
// 创建ARK Runtime客户端
l.client = arkruntime.NewClientWithAkSk(config.AccessKey, config.SecretKey)
global.GVA_LOG.Info("Volcengine client initialized successfully")
return nil
}
// ChatCompletion 非流式聊天完成
func (l *LLMService) ChatCompletion(req request.ChatRequest) (*response.ChatResponse, error) {
if l.client == nil {
if err := l.InitVolcengineClient(); err != nil {
return nil, err
}
}
// 生成请求ID
if req.RequestID == "" {
req.RequestID = uuid.New().String()
}
// 转换消息格式
messages := make([]*model.ChatCompletionMessage, len(req.Messages))
for i, msg := range req.Messages {
messages[i] = &model.ChatCompletionMessage{
Role: msg.Role,
Content: &model.ChatCompletionMessageContent{
StringValue: &msg.Content,
},
}
}
// 设置模型
modelName := req.Model
if modelName == "" {
modelName = global.GVA_CONFIG.Volcengine.DefaultModel
}
// 创建请求
chatReq := &model.ChatCompletionRequest{
Model: modelName,
Messages: messages,
}
// 设置可选参数
if req.Temperature > 0 {
chatReq.Temperature = float32(req.Temperature)
}
if req.MaxTokens > 0 {
chatReq.MaxTokens = req.MaxTokens
}
if req.TopP > 0 {
chatReq.TopP = float32(req.TopP)
}
// 创建上下文
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
// 存储取消函数
l.activeSessions.Store(req.RequestID, cancel)
defer l.activeSessions.Delete(req.RequestID)
// 调用API
resp, err := l.client.CreateChatCompletion(ctx, chatReq)
if err != nil {
global.GVA_LOG.Error("Chat completion failed", zap.Error(err), zap.String("requestID", req.RequestID))
return nil, fmt.Errorf("chat completion failed: %w", err)
}
// 转换响应格式
chatResp := &response.ChatResponse{
ID: req.RequestID,
Object: "chat.completion",
Created: time.Now().Unix(),
Model: modelName,
Choices: make([]response.Choice, len(resp.Choices)),
}
for i, choice := range resp.Choices {
messageContent := ""
if choice.Message.Content != nil && choice.Message.Content.StringValue != nil {
messageContent = *choice.Message.Content.StringValue
}
chatResp.Choices[i] = response.Choice{
Index: i,
Message: response.Message{
Role: choice.Message.Role,
Content: messageContent,
},
FinishReason: string(choice.FinishReason),
}
}
// 设置使用量统计
chatResp.Usage = &response.Usage{
PromptTokens: resp.Usage.PromptTokens,
CompletionTokens: resp.Usage.CompletionTokens,
TotalTokens: resp.Usage.TotalTokens,
}
global.GVA_LOG.Info("Chat completion successful", zap.String("requestID", req.RequestID))
return chatResp, nil
}
// StreamChatCompletion 流式聊天完成
func (l *LLMService) StreamChatCompletion(req request.ChatRequest, eventChan chan<- response.StreamEvent) error {
if l.client == nil {
if err := l.InitVolcengineClient(); err != nil {
return err
}
}
// 生成请求ID
if req.RequestID == "" {
req.RequestID = uuid.New().String()
}
// 转换消息格式
messages := make([]*model.ChatCompletionMessage, len(req.Messages))
for i, msg := range req.Messages {
messages[i] = &model.ChatCompletionMessage{
Role: msg.Role,
Content: &model.ChatCompletionMessageContent{
StringValue: &msg.Content,
},
}
}
// 设置模型
modelName := req.Model
if modelName == "" {
modelName = global.GVA_CONFIG.Volcengine.DefaultModel
}
// 创建流式请求
chatReq := &model.ChatCompletionRequest{
Model: modelName,
Messages: messages,
Stream: true,
}
// 设置可选参数
if req.Temperature > 0 {
chatReq.Temperature = float32(req.Temperature)
}
if req.MaxTokens > 0 {
chatReq.MaxTokens = req.MaxTokens
}
if req.TopP > 0 {
chatReq.TopP = float32(req.TopP)
}
// 创建上下文
ctx, cancel := context.WithTimeout(context.Background(), 300*time.Second) // 流式响应需要更长时间
defer cancel()
// 存储取消函数
l.activeSessions.Store(req.RequestID, cancel)
defer l.activeSessions.Delete(req.RequestID)
// 调用流式API
stream_resp, err := l.client.CreateChatCompletionStream(ctx, chatReq)
if err != nil {
global.GVA_LOG.Error("Stream chat completion failed", zap.Error(err), zap.String("requestID", req.RequestID))
return fmt.Errorf("stream chat completion failed: %w", err)
}
defer stream_resp.Close()
// 处理流式响应
for {
select {
case <-ctx.Done():
// 上下文取消
eventChan <- response.StreamEvent{
Event: "error",
Data: response.ChatResponse{
ID: req.RequestID,
Error: &response.APIError{
Code: "context_cancelled",
Message: "Request was cancelled",
Type: "request_cancelled",
},
},
}
return ctx.Err()
default:
// 接收流式数据
recv, err := stream_resp.Recv()
if err != nil {
if err.Error() == "EOF" {
// 流结束
eventChan <- response.StreamEvent{
Event: "done",
Data: response.ChatResponse{
ID: req.RequestID,
Object: "chat.completion.chunk",
},
}
global.GVA_LOG.Info("Stream chat completion finished", zap.String("requestID", req.RequestID))
return nil
}
global.GVA_LOG.Error("Stream receive error", zap.Error(err), zap.String("requestID", req.RequestID))
eventChan <- response.StreamEvent{
Event: "error",
Data: response.ChatResponse{
ID: req.RequestID,
Error: &response.APIError{
Code: "stream_error",
Message: err.Error(),
Type: "stream_error",
},
},
}
return err
}
// 转换并发送响应
chatResp := response.ChatResponse{
ID: req.RequestID,
Object: "chat.completion.chunk",
Created: time.Now().Unix(),
Model: modelName,
Choices: make([]response.Choice, len(recv.Choices)),
}
for i, choice := range recv.Choices {
chatResp.Choices[i] = response.Choice{
Index: i,
Delta: response.Message{
Role: choice.Delta.Role,
Content: choice.Delta.Content,
},
}
if choice.FinishReason != "" {
chatResp.Choices[i].FinishReason = string(choice.FinishReason)
}
}
eventChan <- response.StreamEvent{
Event: "message",
Data: chatResp,
}
}
}
}
// StopGeneration 停止生成
func (l *LLMService) StopGeneration(req request.StopRequest) (*response.StopResponse, error) {
// 查找并取消对应的请求
if cancelFunc, ok := l.activeSessions.Load(req.RequestID); ok {
if cancel, ok := cancelFunc.(context.CancelFunc); ok {
cancel()
l.activeSessions.Delete(req.RequestID)
global.GVA_LOG.Info("Request stopped successfully", zap.String("requestID", req.RequestID))
return &response.StopResponse{
Success: true,
Message: "Request stopped successfully",
RequestID: req.RequestID,
StoppedAt: time.Now().Unix(),
}, nil
}
}
global.GVA_LOG.Warn("Request not found or already completed", zap.String("requestID", req.RequestID))
return &response.StopResponse{
Success: false,
Message: "Request not found or already completed",
RequestID: req.RequestID,
StoppedAt: time.Now().Unix(),
}, nil
}
// GetActiveSessionsCount 获取活跃会话数量
func (l *LLMService) GetActiveSessionsCount() int {
count := 0
l.activeSessions.Range(func(key, value interface{}) bool {
count++
return true
})
return count
}