Compare commits
36 Commits
9080d2cebe
...
dev未优化
| Author | SHA1 | Date | |
|---|---|---|---|
| 0d52b631b9 | |||
| c22d578e1a | |||
| df26329836 | |||
| 40abf0f606 | |||
| b69e7386e2 | |||
| 1c1db7e30c | |||
| 78114f99c7 | |||
| 9410199fbe | |||
| 1f9a2b9b5f | |||
| e1461cf0f0 | |||
| aa7804656f | |||
| 5494a0c480 | |||
| ee6677c1f8 | |||
| de70d33115 | |||
| b2cad4cac2 | |||
| 05cf1b9828 | |||
| 3fa2896fc3 | |||
| c11a9ad5c8 | |||
| 0bbaddace0 | |||
| 1bcf8f6e10 | |||
| 55eb436639 | |||
| d74559ae74 | |||
| 2548ffc7ac | |||
| 855d5b9abe | |||
| 866b97d098 | |||
| 92092575bc | |||
| a34eb4ea61 | |||
| 15f5761000 | |||
| fee6528f93 | |||
| 35bc3bd6ec | |||
| c49144794d | |||
| 5f98e52b34 | |||
| 6497629fd0 | |||
| c4b8382dbb | |||
| 9dfeb89a72 | |||
| b179fab2a1 |
11
.todo
Normal file
11
.todo
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"tasks": [
|
||||
{
|
||||
"id": "1778047497735wxayc",
|
||||
"title": "Review blocked/reverted work in update.sql",
|
||||
"category": "Blocked",
|
||||
"dueDate": null,
|
||||
"completed": false
|
||||
}
|
||||
]
|
||||
}
|
||||
24
Dockerfile
Normal file
24
Dockerfile
Normal file
@@ -0,0 +1,24 @@
|
||||
# 阶段1: 构建
|
||||
FROM golang:alpine AS builder
|
||||
|
||||
RUN apk add --no-cache git ca-certificates tzdata
|
||||
|
||||
ENV TZ=Asia/Shanghai
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
ENV GO111MODULE=on
|
||||
ENV GOPROXY=https://goproxy.cn,direct
|
||||
ENV CGO_ENABLED=0
|
||||
ENV GOTOOLCHAIN=auto
|
||||
WORKDIR /build
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN go mod download && go mod tidy
|
||||
|
||||
RUN go build -ldflags="-s -w" -o main ./main.go
|
||||
|
||||
|
||||
EXPOSE 3009
|
||||
|
||||
CMD ["./main"]
|
||||
54
README.md
Normal file
54
README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Prompts-Core 提示词核心服务
|
||||
## 项目简介
|
||||
Prompts-Core 是基于 Go 语言开发的**多模态 AI 提示词构建与管理系统**,专注于统一管理各类 AI 模型的提示词模板、维护智能会话上下文、适配主流模型协议,并支持文件解析与外部技能集成,为 AI 应用提供标准化、高效的提示词服务。
|
||||
|
||||
## 核心功能
|
||||
1. **提示词构建引擎**
|
||||
支持文字/图片/音频/向量化/全模态 5 类任务提示词生成,提供完整流程、分步节点两种构建模式,支持超大内容按 Token 自动分批处理。
|
||||
2. **智能会话管理**
|
||||
基于缓存实现高效会话存储,自动控制会话轮数与过期时间,保障上下文连贯性。
|
||||
3. **多模型协议适配**
|
||||
动态适配 OpenAI、DeepSeek、Qwen、Gemini 等主流 AI 模型协议,支持角色、字段、消息顺序灵活映射。
|
||||
4. **文件与技能集成**
|
||||
自动提取文本、ZIP 压缩包内容,支持加载外部 Markdown 技能配置,扩展服务能力。
|
||||
5. **异步任务调度**
|
||||
支持异步任务处理、状态轮询与回调通知,自带可配置重试机制。
|
||||
|
||||
## 技术架构
|
||||
- 开发语言:Go 1.26.0
|
||||
- Web 框架:GoFrame v2.10.0
|
||||
- 核心存储:Redis(会话缓存)
|
||||
- 服务组件:Consul(服务注册)、Jaeger(链路追踪)
|
||||
- 调用链路:客户端 → Prompts-Core → 模型网关 → AI 模型
|
||||
|
||||
## 快速开始
|
||||
### 环境要求
|
||||
Go 1.26+、Redis、已部署模型网关服务
|
||||
|
||||
### 启动步骤
|
||||
1. 克隆项目代码
|
||||
2. 完成项目配置文件修改
|
||||
3. 执行命令启动服务:
|
||||
```bash
|
||||
go run main.go
|
||||
```
|
||||
|
||||
## API 接口
|
||||
### 基础信息
|
||||
- 服务地址:`http://{host}:3009`
|
||||
- 请求类型:`application/json`
|
||||
- 认证方式:请求头携带 `Authorization`、`X-User`
|
||||
|
||||
### 核心接口
|
||||
1. **提示词拼接接口**
|
||||
- 地址:`POST /composeMessages`
|
||||
- 功能:构建提示词并调用模型服务,同步返回结果
|
||||
2. **任务状态查询**
|
||||
- 地址:`GET /getComposeTask`
|
||||
- 功能:根据任务 ID 查询处理状态与结果
|
||||
3. **任务回调接口**
|
||||
- 地址:`GET /composeMessagesCallback/prompts-core`
|
||||
- 功能:接收模型服务处理完成回调
|
||||
4. **会话同步接口**
|
||||
- 地址:`POST /sessionCallback`
|
||||
- 功能:同步更新会话上下文历史
|
||||
34
common/util/config.go
Normal file
34
common/util/config.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// GetServerName 获取服务名称
|
||||
func GetServerName(ctx context.Context) string {
|
||||
return g.Cfg().MustGet(ctx, "server.name", "").String()
|
||||
}
|
||||
|
||||
// GetModelPrompt 获取请求模型的提示词
|
||||
func GetModelPrompt(ctx context.Context, modelType int) string {
|
||||
key := "modelPrompts.types." + gconv.String(modelType)
|
||||
return g.Cfg().MustGet(ctx, key, "").String()
|
||||
}
|
||||
|
||||
// GetBuildPrompt 获取节点构建提示词
|
||||
func GetBuildPrompt(ctx context.Context) string {
|
||||
return g.Cfg().MustGet(ctx, "nodePrompts", "").String()
|
||||
}
|
||||
|
||||
// GetMaxRounds 获取最大轮数配置
|
||||
func GetMaxRounds(ctx context.Context) int {
|
||||
return g.Cfg().MustGet(ctx, "session.maxRounds", 10).Int()
|
||||
}
|
||||
|
||||
// GetExpireMinutes 获取过期时间配置
|
||||
func GetExpireMinutes(ctx context.Context) int {
|
||||
return g.Cfg().MustGet(ctx, "session.expireMinutes", 30).Int()
|
||||
}
|
||||
96
common/util/files.go
Normal file
96
common/util/files.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// AllowedMIMEPrefixes 允许的文本类 MIME 类型前缀
|
||||
AllowedMIMEPrefixes = []string{
|
||||
"text/",
|
||||
"application/json",
|
||||
"application/xml",
|
||||
"application/javascript",
|
||||
"application/x-yaml",
|
||||
"application/yaml",
|
||||
"application/toml",
|
||||
"application/x-httpd-php",
|
||||
"application/x-sh",
|
||||
"application/x-python",
|
||||
"application/x-perl",
|
||||
"application/x-ruby",
|
||||
}
|
||||
|
||||
// BannedExtensions 禁止的文件扩展名
|
||||
BannedExtensions = map[string]bool{
|
||||
".png": true, ".jpg": true, ".jpeg": true, ".gif": true, ".bmp": true,
|
||||
".webp": true, ".svg": true, ".ico": true, ".tiff": true, ".tif": true,
|
||||
".mp3": true, ".wav": true, ".ogg": true, ".flac": true, ".aac": true,
|
||||
".wma": true, ".m4a": true,
|
||||
".mp4": true, ".avi": true, ".mkv": true, ".mov": true, ".wmv": true,
|
||||
".flv": true, ".webm": true,
|
||||
".tar": true, ".gz": true, ".rar": true, ".7z": true,
|
||||
".exe": true, ".dll": true, ".so": true, ".bin": true, ".dat": true,
|
||||
".class": true, ".pyc": true,
|
||||
".pdf": true, ".doc": true, ".docx": true, ".xls": true, ".xlsx": true,
|
||||
".ppt": true, ".pptx": true,
|
||||
}
|
||||
|
||||
symbolCleaner = regexp.MustCompile(`[\x00-\x08\x0B\x0C\x0E-\x1F]`)
|
||||
multiNewlines = regexp.MustCompile(`\n{3,}`)
|
||||
)
|
||||
|
||||
// SanitizeURL 清洗 URL 字符串
|
||||
func SanitizeURL(raw string) string {
|
||||
s := strings.TrimSpace(raw)
|
||||
s = strings.Trim(s, "`\"")
|
||||
return s
|
||||
}
|
||||
|
||||
// CleanSymbols 清洗文本中的控制字符和多余空行
|
||||
func CleanSymbols(text string) string {
|
||||
text = symbolCleaner.ReplaceAllString(text, "")
|
||||
text = strings.ReplaceAll(text, "\r\n", "\n")
|
||||
text = strings.ReplaceAll(text, "\r", "\n")
|
||||
text = multiNewlines.ReplaceAllString(text, "\n\n")
|
||||
return strings.TrimSpace(text)
|
||||
}
|
||||
|
||||
// IsBannedExtension 判断是否为禁止的文件扩展名
|
||||
func IsBannedExtension(url string) bool {
|
||||
ext := extractExtension(url)
|
||||
return BannedExtensions[ext]
|
||||
}
|
||||
|
||||
// IsZipExtension 判断是否为 zip 文件
|
||||
func IsZipExtension(url string) bool {
|
||||
ext := extractExtension(url)
|
||||
return ext == ".zip"
|
||||
}
|
||||
|
||||
// IsReadableContentType 判断是否为可读的文本类型
|
||||
func IsReadableContentType(contentType string) bool {
|
||||
if contentType == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
ct := strings.ToLower(contentType)
|
||||
for _, prefix := range AllowedMIMEPrefixes {
|
||||
if strings.HasPrefix(ct, prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// extractExtension 提取文件扩展名并清理查询参数
|
||||
func extractExtension(url string) string {
|
||||
ext := strings.ToLower(filepath.Ext(url))
|
||||
if idx := strings.Index(ext, "?"); idx != -1 {
|
||||
ext = ext[:idx]
|
||||
}
|
||||
return ext
|
||||
}
|
||||
67
common/util/headers.go
Normal file
67
common/util/headers.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/utils"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// AsyncCtx 固化异步上下文中的 token 和用户信息,避免请求结束后丢失
|
||||
func AsyncCtx(ctx context.Context) context.Context {
|
||||
asyncCtx := context.WithoutCancel(ctx)
|
||||
|
||||
if r := g.RequestFromCtx(ctx); r != nil {
|
||||
if token := r.Header.Get("Authorization"); token != "" {
|
||||
asyncCtx = context.WithValue(asyncCtx, "token", token)
|
||||
}
|
||||
if userInfo := r.Header.Get("X-User-Info"); userInfo != "" {
|
||||
asyncCtx = context.WithValue(asyncCtx, "xUserInfo", userInfo)
|
||||
}
|
||||
}
|
||||
|
||||
if user, err := utils.GetUserInfo(ctx); err == nil && user != nil {
|
||||
asyncCtx = context.WithValue(asyncCtx, "user", user)
|
||||
}
|
||||
|
||||
return asyncCtx
|
||||
}
|
||||
|
||||
// ForwardHeaders 透传调用链路的头信息,优先使用 ctx 中的固化值
|
||||
func ForwardHeaders(ctx context.Context) map[string]string {
|
||||
headers := make(map[string]string)
|
||||
|
||||
setHeaderFromContext(headers, ctx, "Authorization", "token")
|
||||
setHeaderFromContext(headers, ctx, "X-User-Info", "xUserInfo")
|
||||
|
||||
fallbackToRequestHeaders(headers, ctx)
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
// setHeaderFromContext 从上下文中设置 header
|
||||
func setHeaderFromContext(headers map[string]string, ctx context.Context, headerKey, ctxKey string) {
|
||||
if value, ok := ctx.Value(ctxKey).(string); ok && value != "" {
|
||||
headers[headerKey] = value
|
||||
}
|
||||
}
|
||||
|
||||
// fallbackToRequestHeaders 从请求头中获取作为兜底
|
||||
func fallbackToRequestHeaders(headers map[string]string, ctx context.Context) {
|
||||
r := g.RequestFromCtx(ctx)
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if headers["Authorization"] == "" {
|
||||
if token := r.Header.Get("Authorization"); token != "" {
|
||||
headers["Authorization"] = token
|
||||
}
|
||||
}
|
||||
|
||||
if headers["X-User-Info"] == "" {
|
||||
if userInfo := r.Header.Get("X-User-Info"); userInfo != "" {
|
||||
headers["X-User-Info"] = userInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
81
common/util/json.go
Normal file
81
common/util/json.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// MergeConsult 将 consult 附件合并到模型生成的 messages 结构中
|
||||
func MergeConsult(req map[string]any, messages map[string]any, extendMapping map[string]any) map[string]any {
|
||||
if len(req) == 0 || len(messages) == 0 || len(extendMapping) == 0 {
|
||||
return messages
|
||||
}
|
||||
|
||||
consult := gconv.Interfaces(req["consult"])
|
||||
if len(consult) == 0 {
|
||||
return messages
|
||||
}
|
||||
|
||||
targetPath := gconv.String(extendMapping["target_content_path"])
|
||||
templates := gconv.Map(extendMapping["attachment_templates"])
|
||||
if targetPath == "" || len(templates) == 0 {
|
||||
return messages
|
||||
}
|
||||
|
||||
msgJson := gjson.New(messages)
|
||||
|
||||
// rounds 路径修正
|
||||
if !msgJson.Get("rounds.0").IsNil() {
|
||||
targetPath = "rounds.0." + targetPath
|
||||
}
|
||||
|
||||
// 遍历追加
|
||||
for _, item := range consult {
|
||||
itemJson := gjson.New(item)
|
||||
itemType := itemJson.Get("type").String()
|
||||
tmpl := gconv.Map(templates[itemType])
|
||||
if itemType == "" || len(tmpl) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
attachment := buildAttachment(tmpl, itemJson.Get("url").String())
|
||||
if attachment == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
idx := len(msgJson.Get(targetPath).Array())
|
||||
_ = msgJson.Set(fmt.Sprintf("%s.%d", targetPath, idx), attachment)
|
||||
}
|
||||
|
||||
return msgJson.Map()
|
||||
}
|
||||
|
||||
func buildAttachment(tmpl map[string]any, url string) map[string]any {
|
||||
typ := gconv.String(tmpl["type"])
|
||||
if typ == "" || url == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
body := gconv.Map(tmpl["body"])
|
||||
fillEmptyInPlace(body, url)
|
||||
|
||||
return map[string]any{
|
||||
"type": typ,
|
||||
typ: body,
|
||||
}
|
||||
}
|
||||
|
||||
func fillEmptyInPlace(m map[string]any, value string) {
|
||||
for k, v := range m {
|
||||
switch vv := v.(type) {
|
||||
case string:
|
||||
if vv == "" {
|
||||
m[k] = value
|
||||
}
|
||||
case map[string]any:
|
||||
fillEmptyInPlace(vv, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
57
common/util/mapping.go
Normal file
57
common/util/mapping.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// ReverseMap 映射 payload 到 mapping
|
||||
func ReverseMap(mapping map[string]any, payload map[string]any) map[string]any {
|
||||
jsonObj := gjson.New("{}")
|
||||
for path, defaultValue := range mapping {
|
||||
val := gjson.New(payload).Get(path)
|
||||
if !val.IsNil() {
|
||||
_ = jsonObj.Set(path, val.Val())
|
||||
} else if defaultValue != nil {
|
||||
_ = jsonObj.Set(path, defaultValue)
|
||||
}
|
||||
}
|
||||
return jsonObj.Map()
|
||||
}
|
||||
|
||||
// ExtractUserText 从 messages 中提取所有 user 文本
|
||||
func ExtractUserText(messages map[string]any) map[string]any {
|
||||
msgJson := gjson.New(messages)
|
||||
|
||||
msgs := msgJson.Get("rounds.0.messages")
|
||||
if msgs.IsNil() {
|
||||
msgs = msgJson.Get("messages")
|
||||
}
|
||||
var texts []string
|
||||
for _, m := range msgs.Array() {
|
||||
msg := gjson.New(m)
|
||||
if msg.Get("role").String() != "user" {
|
||||
continue
|
||||
}
|
||||
content := msg.Get("content").Val()
|
||||
switch c := content.(type) {
|
||||
case string:
|
||||
texts = append(texts, c)
|
||||
case []any:
|
||||
for _, item := range c {
|
||||
if m, ok := item.(map[string]any); ok {
|
||||
if t := gconv.String(m["text"]); t != "" {
|
||||
texts = append(texts, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"role": "user",
|
||||
"content": strings.Join(texts, "\n"),
|
||||
}
|
||||
}
|
||||
229
common/util/token.go
Normal file
229
common/util/token.go
Normal file
@@ -0,0 +1,229 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
)
|
||||
|
||||
var (
|
||||
enWordRegex = regexp.MustCompile(`[A-Za-z]+`)
|
||||
punctRegex = regexp.MustCompile(`[[:punct:]]`)
|
||||
)
|
||||
|
||||
// TokenConfig Token计算配置
|
||||
type TokenConfig struct {
|
||||
ZhRatio float64 `json:"zh_ratio"`
|
||||
EnRatio float64 `json:"en_ratio"`
|
||||
SpaceRatio float64 `json:"space_ratio"`
|
||||
PunctuationRatio float64 `json:"punctuation_ratio"`
|
||||
MaxWindowSize int `json:"max_window_size"`
|
||||
ReserveRatio float64 `json:"reserve_ratio"`
|
||||
MinReserve int `json:"min_reserve"`
|
||||
}
|
||||
|
||||
// CalculateTokens 计算文本token数
|
||||
func CalculateTokens(text string, tokenConfig any) int {
|
||||
config := parseConfig(tokenConfig)
|
||||
if config == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
if text == "" {
|
||||
return 0
|
||||
}
|
||||
|
||||
zhCount := countChineseChars(text)
|
||||
enCount := countEnglishWords(text)
|
||||
spaceCount := strings.Count(text, " ")
|
||||
punctCount := countPunctuation(text)
|
||||
|
||||
totalTokens := int(
|
||||
float64(zhCount)*config.ZhRatio +
|
||||
float64(enCount)*config.EnRatio +
|
||||
float64(spaceCount)*config.SpaceRatio +
|
||||
float64(punctCount)*config.PunctuationRatio,
|
||||
)
|
||||
|
||||
return totalTokens
|
||||
}
|
||||
|
||||
// CountToken 计算token是否超出窗口限制
|
||||
// 返回: true - 未超出(可用), false - 已超出(不可用)
|
||||
func CountToken(text string, tokenConfig any) bool {
|
||||
config := parseConfig(tokenConfig)
|
||||
if config == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
estimatedTokens := CalculateTokens(text, tokenConfig)
|
||||
availableWindow := GetAvailableWindow(tokenConfig)
|
||||
|
||||
return estimatedTokens <= availableWindow
|
||||
}
|
||||
|
||||
// GetAvailableWindow 获取可用窗口大小
|
||||
func GetAvailableWindow(tokenConfig any) int {
|
||||
config := parseConfig(tokenConfig)
|
||||
if config == nil {
|
||||
return 4096
|
||||
}
|
||||
|
||||
reserveByRatio := int(float64(config.MaxWindowSize) * config.ReserveRatio)
|
||||
reserve := reserveByRatio
|
||||
|
||||
if config.MinReserve > reserve {
|
||||
reserve = config.MinReserve
|
||||
}
|
||||
|
||||
available := config.MaxWindowSize - reserve
|
||||
if available < 0 {
|
||||
available = 0
|
||||
}
|
||||
|
||||
return available
|
||||
}
|
||||
|
||||
// GetMaxWindowSize 获取模型最大窗口大小
|
||||
func GetMaxWindowSize(tokenConfig any) int {
|
||||
config := parseConfig(tokenConfig)
|
||||
if config == nil {
|
||||
return 4096
|
||||
}
|
||||
|
||||
return config.MaxWindowSize
|
||||
}
|
||||
|
||||
// CheckUserFormWithinWindow 校验 UserForm 是否在窗口大小内
|
||||
// 返回: isValid, exceedTokens, error
|
||||
func CheckUserFormWithinWindow(userForm []map[string]any, tokenConfig any) (bool, int, error) {
|
||||
config := parseConfig(tokenConfig)
|
||||
if config == nil || len(userForm) == 0 {
|
||||
return true, 0, nil
|
||||
}
|
||||
|
||||
totalTokens := calculateUserFormTokens(userForm, tokenConfig)
|
||||
availableWindow := GetAvailableWindow(tokenConfig)
|
||||
|
||||
if totalTokens > availableWindow {
|
||||
return false, totalTokens - availableWindow, nil
|
||||
}
|
||||
|
||||
return true, 0, nil
|
||||
}
|
||||
|
||||
// CheckUserFormBatchWithinWindow 检查 UserForm 分批是否在窗口内
|
||||
// 返回: 需要拆分的批次数, 每批的token数, 错误
|
||||
func CheckUserFormBatchWithinWindow(userForm []map[string]any, tokenConfig any) (int, []int, error) {
|
||||
config := parseConfig(tokenConfig)
|
||||
if config == nil || len(userForm) == 0 {
|
||||
return 1, nil, nil
|
||||
}
|
||||
|
||||
availableWindow := GetAvailableWindow(tokenConfig)
|
||||
|
||||
batches := 1
|
||||
currentTokens := 0
|
||||
batchTokens := make([]int, 0)
|
||||
|
||||
for _, item := range userForm {
|
||||
itemStr := fmt.Sprintf("%v", item)
|
||||
itemTokens := CalculateTokens(itemStr, tokenConfig)
|
||||
|
||||
if currentTokens+itemTokens > availableWindow {
|
||||
batchTokens = append(batchTokens, currentTokens)
|
||||
batches++
|
||||
currentTokens = itemTokens
|
||||
} else {
|
||||
currentTokens += itemTokens
|
||||
}
|
||||
}
|
||||
|
||||
if currentTokens > 0 {
|
||||
batchTokens = append(batchTokens, currentTokens)
|
||||
}
|
||||
|
||||
return batches, batchTokens, nil
|
||||
}
|
||||
|
||||
// parseConfig 解析配置
|
||||
func parseConfig(tokenConfig any) *TokenConfig {
|
||||
if tokenConfig == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch v := tokenConfig.(type) {
|
||||
case *gvar.Var:
|
||||
return parseGVarConfig(v)
|
||||
case map[string]any:
|
||||
return parseMapConfig(v)
|
||||
case *TokenConfig:
|
||||
return v
|
||||
case TokenConfig:
|
||||
return &v
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// parseGVarConfig 解析 GVar 配置
|
||||
func parseGVarConfig(v *gvar.Var) *TokenConfig {
|
||||
if v.IsNil() {
|
||||
return nil
|
||||
}
|
||||
|
||||
mapVal := v.Map()
|
||||
if mapVal == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
config := &TokenConfig{}
|
||||
data, _ := json.Marshal(mapVal)
|
||||
json.Unmarshal(data, config)
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// parseMapConfig 解析 Map 配置
|
||||
func parseMapConfig(v map[string]any) *TokenConfig {
|
||||
config := &TokenConfig{}
|
||||
data, _ := json.Marshal(v)
|
||||
json.Unmarshal(data, config)
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// countChineseChars 统计中文字符数量
|
||||
func countChineseChars(text string) int {
|
||||
count := 0
|
||||
for _, r := range text {
|
||||
if unicode.Is(unicode.Han, r) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// countEnglishWords 统计英文单词数量
|
||||
func countEnglishWords(text string) int {
|
||||
return len(enWordRegex.FindAllString(text, -1))
|
||||
}
|
||||
|
||||
// countPunctuation 统计标点符号数量
|
||||
func countPunctuation(text string) int {
|
||||
return len(punctRegex.FindAllString(text, -1))
|
||||
}
|
||||
|
||||
// calculateUserFormTokens 计算 UserForm 总 token 数
|
||||
func calculateUserFormTokens(userForm []map[string]any, tokenConfig any) int {
|
||||
totalTokens := 0
|
||||
for _, item := range userForm {
|
||||
itemStr := fmt.Sprintf("%v", item)
|
||||
totalTokens += CalculateTokens(itemStr, tokenConfig)
|
||||
}
|
||||
return totalTokens
|
||||
}
|
||||
114
config.yml
Normal file
114
config.yml
Normal file
@@ -0,0 +1,114 @@
|
||||
server:
|
||||
address: ":3009"
|
||||
name: "prompts-core"
|
||||
workerId: 1 # 雪花算法 worker ID(用于 common/db/gfdb)
|
||||
|
||||
# PostgreSQL(GoFrame driver pgsql)
|
||||
database:
|
||||
default:
|
||||
- type: "pgsql"
|
||||
host: "116.204.74.41"
|
||||
port: "15432"
|
||||
user: "postgres"
|
||||
pass: "Bjang09@686^*^"
|
||||
name: "model-gateway"
|
||||
prefix: "" # (可选)表名前缀
|
||||
role: "master" # (可选)数据库主从角色(master/slave),默认为master。如果不使用应用主从机制请不配置或留空即可。
|
||||
debug: true # (可选)开启调试模式
|
||||
dryRun: false # (可选)ORM空跑(只读不写)
|
||||
charset: "utf8" # (可选)数据库编码(如: utf8mb4/utf8/gbk/gb2312),一般设置为utf8mb4。默认为utf8。
|
||||
timezone: "Asia/Shanghai" # (可选)时区配置,例如:Local
|
||||
maxIdle: 5 # (可选)连接池最大闲置的连接数(默认10)
|
||||
maxOpen: 20 # (可选)连接池最大打开的连接数(默认无限制)
|
||||
maxLifetime: "30s" # (可选)连接对象可重复使用的时间长度(默认30秒)
|
||||
maxIdleConnTime: "30s" # (可选,v2.10新增)连接池中空闲连接的最大生存时间(默认30秒)。可以通过配置文件或SetConnMaxIdleTime方法设置,避免长时间空闲连接占用资源。
|
||||
createdAt: "created_at" # (可选)自动创建时间字段名称
|
||||
updatedAt: "updated_at" # (可选)自动更新时间字段名称
|
||||
deletedAt: "deleted_at" # (可选)软删除时间字段名称
|
||||
timeMaintainDisabled: false # (可选)是否完全关闭时间更新特性,为true时CreatedAt/UpdatedAt/DeletedAt都将失效
|
||||
model_gateway:
|
||||
- type: "pgsql"
|
||||
host: "116.204.74.41"
|
||||
port: "15432"
|
||||
user: "postgres"
|
||||
pass: "Bjang09@686^*^"
|
||||
name: "model-gateway"
|
||||
prefix: ""
|
||||
role: "master"
|
||||
debug: true
|
||||
dryRun: false
|
||||
charset: "utf8"
|
||||
timezone: "Asia/Shanghai"
|
||||
maxIdle: 5
|
||||
maxOpen: 20
|
||||
maxLifetime: "30s"
|
||||
maxIdleConnTime: "30s"
|
||||
createdAt: "created_at"
|
||||
updatedAt: "updated_at"
|
||||
deletedAt: "deleted_at"
|
||||
timeMaintainDisabled: false
|
||||
|
||||
redis:
|
||||
default:
|
||||
address: 192.168.3.30:6379
|
||||
db: 0
|
||||
|
||||
consul:
|
||||
address: 192.168.3.30:8500
|
||||
|
||||
jaeger:
|
||||
addr: 192.168.3.30:4318
|
||||
|
||||
task:
|
||||
waitTimeoutSeconds: 600 # /composeMessages 同步等待最终结果的最长时间(秒)
|
||||
|
||||
session:
|
||||
maxRounds: 10 # 最大轮数
|
||||
expireTime: 1800 # 过期时间(秒),30分钟
|
||||
|
||||
# 文件处理配置
|
||||
userFiles:
|
||||
zipMaxSizeMB: 10 # zip 下载最大大小(MB)
|
||||
zipEntryMaxSizeKB: 500 # zip 内单文件最大读取大小(KB)
|
||||
textFileMaxSizeKB: 500 # 普通文本文件最大读取大小(KB)
|
||||
httpTimeoutSec: 8 # HTTP 请求超时(秒)
|
||||
skillFiles:
|
||||
httpTimeoutSec: 500 # zip 下载超时(秒)
|
||||
zipMaxSizeMB: 10 # zip 最大下载大小(MB)
|
||||
mdMaxSizeKB: 5000 # 单个 md 文件最大读取大小(KB)
|
||||
promptsRetry:
|
||||
maxRetryTimes: 3
|
||||
|
||||
modelPrompts:
|
||||
types:
|
||||
100: |
|
||||
你是一个智能文字处理助手,专注于文本理解、文本创作、文本优化与语言表达任务,能够根据不同场景完成文章撰写、商业文案、报告总结、邮件通知、脚本创作、内容改写、信息提炼、语言翻译等多种文字处理工作,并能够理解上下文语义关系,保持内容逻辑完整、结构清晰、表达自然。
|
||||
在执行文本任务时,你需要以专业内容创作者、编辑顾问、语言优化专家的身份完成输出,严格保证语言准确性、逻辑连贯性、表达一致性与阅读体验,根据不同用户场景自动适配正式、口语化、专业化、营销化等表达风格,同时避免空洞表达、重复描述与机械化生成内容。
|
||||
当用户提供具体需求时,需要结合用户输入、上下文信息、参数条件与目标场景生成最终文本结果;若涉及改写、扩写、摘要、总结、标题、营销内容等任务,需要保证核心语义不偏离,并根据用户真实目的完成结构化输出。
|
||||
200: |
|
||||
你是一个智能图片处理助手,专注于视觉内容生成、图像编辑、画面分析与风格控制任务,能够根据文字描述生成不同风格的图片内容,包括写实、插画、动漫、水彩、电影感、商业海报等多种视觉形式,并支持图片局部修改、风格迁移、画面扩展、背景处理与视觉增强等操作。
|
||||
在执行图片相关任务时,你需要以专业视觉设计师、插画师、摄影指导、美术导演的身份进行画面构建,重点关注主体构图、色彩关系、光影氛围、镜头语言、视觉层次与整体风格统一性,确保生成结果具备明确视觉主题与稳定审美表现,而不是简单关键词堆砌。
|
||||
当用户提供图片需求时,需要结合用户描述、场景用途、风格方向、尺寸比例、主体元素、氛围要求等信息生成完整视觉方案;若存在图片编辑任务,则必须保留原图核心特征,仅对用户指定区域或效果进行修改。
|
||||
300: |
|
||||
你是一个智能音频处理助手,专注于语音生成、语音识别、音频分析与声音编辑任务,能够完成文字转语音、语音转文本、多语言识别、音频降噪、音色处理、混音剪辑、情绪识别与声音特征分析等多种音频相关工作,并能够根据不同场景匹配对应语音风格与声音表现形式。
|
||||
在执行音频任务时,你需要以专业配音导演、声音工程师、语音分析专家、后期音频制作人员的身份进行处理,重点保证语音自然度、情绪一致性、识别准确率、音频清晰度与输出稳定性,同时确保不同格式、采样率与播放场景下具备良好兼容性。
|
||||
当用户提供具体音频需求时,需要结合音色、语速、语言类型、情绪风格、背景环境、输出格式等参数完成对应处理;若涉及语音识别或音频分析,则需要尽可能保留原始语义与声音特征,并明确标注不确定内容。
|
||||
400: |
|
||||
你是一个智能向量化处理助手,专注于文本向量化、语义检索、知识索引、相似度计算与语义聚类任务,能够将文本内容转换为高维语义向量,并基于向量相似度完成语义搜索、知识召回、内容聚类、文档匹配与知识库构建等处理流程。
|
||||
在执行向量化任务时,你需要以语义检索工程师、知识库架构师、AI检索系统专家的身份进行处理,重点保证语义表达准确性、向量一致性、检索稳定性与召回有效性,同时确保不同文本之间的语义关系能够被正确表达与计算。
|
||||
当用户提供文本集合、知识内容或检索需求时,需要结合文本上下文、主题方向、检索目标、相似度要求与业务场景生成最终结果;若涉及聚类或知识库构建,则必须明确类别关系、索引结构与召回逻辑。
|
||||
500: |
|
||||
你是一个全模态智能处理助手,能够同时理解、分析与生成文本、图片、音频、视频等多种模态内容,并支持跨模态转换、多模态融合推理、联合内容生成与复杂场景交互,能够根据不同输入形式自动匹配最合理的处理策略与输出方式。
|
||||
在执行多模态任务时,你需要以全链路AI内容架构师、多模态交互专家、综合内容生成系统的身份完成处理,重点保证不同模态之间的语义一致性、风格统一性、信息完整性与交互连贯性,避免出现跨模态语义断裂或输出不一致的问题。
|
||||
当用户提供混合输入内容时,需要结合文本、图片、音频、视频等多种信息共同分析用户真实目标,并根据任务场景自动决定最终输出形式;若涉及跨模态生成,则必须保证生成结果能够准确映射原始语义与核心信息。
|
||||
|
||||
nodePrompts: |
|
||||
你是流程路由助手,你的任务是根据上下文,选择一个正确的节点ID返回。
|
||||
规则:
|
||||
1. 只允许从下面的可选节点ID列表中选择一个返回
|
||||
2. 不要返回任何多余文字、标点、解释、标题
|
||||
3. 只返回纯节点ID
|
||||
可选节点ID(ID: 节点描述):
|
||||
%s
|
||||
上下文内容:
|
||||
%s
|
||||
17
consts/public/public.go
Normal file
17
consts/public/public.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package public
|
||||
|
||||
const (
|
||||
ComposeStatusPending = "pending"
|
||||
ComposeStatusSuccess = "success"
|
||||
ComposeStatusFailed = "failed"
|
||||
)
|
||||
|
||||
const (
|
||||
BuildTypePrompt = 1 //提示词构建
|
||||
BuildTypeNode = 2 //节点构建
|
||||
BuildTypeStruct = 3 //结构构建
|
||||
)
|
||||
|
||||
const (
|
||||
ModelTypeInference = 100 // 推理模型
|
||||
)
|
||||
12
consts/public/table_name.go
Normal file
12
consts/public/table_name.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package public
|
||||
|
||||
const (
|
||||
DbNameModelGateway = "model_gateway" //数据库名称
|
||||
)
|
||||
|
||||
const (
|
||||
TableNameModel = "asynch_models" // 模型表
|
||||
TableNameComposeTask = "prompts_compose_task" // 拼接提示词任务记录表
|
||||
TableNameComposeSession = "prompts_compose_session" // 拼接提示词会话记录表
|
||||
TableNameProviderProtocol = "prompts_provider_protocol"
|
||||
)
|
||||
28
controller/prompt_compose_controller.go
Normal file
28
controller/prompt_compose_controller.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"prompts-core/model/dto"
|
||||
promptService "prompts-core/service/prompt"
|
||||
)
|
||||
|
||||
type prompt struct{}
|
||||
|
||||
// Prompt 提示词配置控制器
|
||||
var Prompt = new(prompt)
|
||||
|
||||
// ComposeMessages 调用 model-gateway 异步任务并同步等待结果,
|
||||
func (c *prompt) ComposeMessages(ctx context.Context, req *dto.ComposeMessagesReq) (res *dto.ComposeMessagesRes, err error) {
|
||||
return promptService.ComposeMessages(ctx, req)
|
||||
}
|
||||
|
||||
// Callback model-gateway 提示词回调
|
||||
func (c *prompt) Callback(ctx context.Context, req *dto.CallbackReq) (res *dto.CallbackRes, err error) {
|
||||
err = promptService.Callback(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
// GetComposeTask 查询拼接任务结果
|
||||
func (c *prompt) GetComposeTask(ctx context.Context, req *dto.GetComposeTaskReq) (res *dto.GetComposeTaskRes, err error) {
|
||||
return promptService.GetComposeTask(ctx, req.TaskId)
|
||||
}
|
||||
36
controller/prompt_session_controller.go
Normal file
36
controller/prompt_session_controller.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// ============================================
|
||||
// controller/session.go
|
||||
// ============================================
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"prompts-core/model/dto"
|
||||
sessionService "prompts-core/service/session"
|
||||
)
|
||||
|
||||
type session struct{}
|
||||
|
||||
var Session = new(session)
|
||||
|
||||
// SessionCallback 会话回调
|
||||
func (c *session) SessionCallback(ctx context.Context, req *dto.SessionCallbackReq) (res *dto.SessionCallbackRes, err error) {
|
||||
return sessionService.Callback(ctx, req)
|
||||
}
|
||||
|
||||
// GetHistoryList 获取历史列表(前端列表)
|
||||
func (c *session) GetHistoryList(ctx context.Context, req *dto.GetHistoryListReq) (res *dto.GetHistoryListRes, err error) {
|
||||
return sessionService.GetHistoryList(ctx, req)
|
||||
}
|
||||
|
||||
// DeleteMessages 批量删除消息
|
||||
func (c *session) DeleteMessages(ctx context.Context, req *dto.DeleteMessagesReq) (res *dto.DeleteMessagesRes, err error) {
|
||||
return sessionService.DeleteMessages(ctx, req)
|
||||
}
|
||||
|
||||
// DeleteSession 删除整个会话
|
||||
func (c *session) DeleteSession(ctx context.Context, req *dto.DeleteSessionReq) (res *dto.DeleteSessionRes, err error) {
|
||||
return sessionService.DeleteSession(ctx, req)
|
||||
}
|
||||
124
dao/compose_session_dao.go
Normal file
124
dao/compose_session_dao.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"prompts-core/consts/public"
|
||||
"prompts-core/model/entity"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
|
||||
)
|
||||
|
||||
var ComposeSession = &composeSessionDao{}
|
||||
|
||||
type composeSessionDao struct{}
|
||||
|
||||
// Insert 插入
|
||||
func (d *composeSessionDao) Insert(ctx context.Context, req *entity.ComposeSession) (id int64, err error) {
|
||||
r, err := gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameComposeSession).
|
||||
Insert(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
// Update 更新
|
||||
func (d *composeSessionDao) Update(ctx context.Context, req *entity.ComposeSession) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameComposeSession).
|
||||
OmitEmpty().
|
||||
Data(&req).
|
||||
Where(entity.ComposeSessionCol.Id, req.Id).
|
||||
Update()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.RowsAffected()
|
||||
}
|
||||
|
||||
// List 查询编排会话列表
|
||||
func (d *composeSessionDao) List(ctx context.Context, req *entity.ComposeSession, page, size int, fields ...string) (list []*entity.ComposeSession, total int, err error) {
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if size <= 0 {
|
||||
size = 10
|
||||
}
|
||||
model := gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameComposeSession).
|
||||
Fields(fields).
|
||||
OmitEmpty()
|
||||
model.Where(entity.ComposeSessionCol.Creator, req.Creator)
|
||||
model.Where(entity.ComposeSessionCol.SessionId, req.SessionId)
|
||||
model.OrderDesc(entity.ComposeSessionCol.CreatedAt)
|
||||
model.Page(page, size)
|
||||
r, total, err := model.AllAndCount(false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = r.Structs(&list)
|
||||
return
|
||||
}
|
||||
|
||||
// Get 查询编排会话
|
||||
func (d *composeSessionDao) Get(ctx context.Context, req *entity.ComposeSession, fields ...string) (m *entity.ComposeSession, err error) {
|
||||
r, err := gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameComposeSession).
|
||||
OmitEmpty().
|
||||
Where(entity.ComposeSessionCol.Id, req.Id).
|
||||
Where(entity.ComposeSessionCol.Creator, req.Creator).
|
||||
Where(entity.ComposeSessionCol.SessionId, req.SessionId).
|
||||
Fields(fields).One()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.IsEmpty() {
|
||||
return
|
||||
}
|
||||
err = r.Struct(&m)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete 删除编排会话
|
||||
func (d *composeSessionDao) Delete(ctx context.Context, req *entity.ComposeSession) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameComposeSession).
|
||||
OmitEmpty().
|
||||
Where(entity.ComposeSessionCol.Id, req.Id).
|
||||
Where(entity.ComposeSessionCol.Creator, req.Creator).
|
||||
Where(entity.ComposeSessionCol.SessionId, req.SessionId).
|
||||
Delete()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.RowsAffected()
|
||||
}
|
||||
|
||||
// ListByIds 根据 ID 列表批量查询
|
||||
func (d *composeSessionDao) ListByIds(ctx context.Context, ids []int64, creator, sessionId string) (list []*entity.ComposeSession, err error) {
|
||||
if len(ids) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
r, err := gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameComposeSession).
|
||||
WhereIn(entity.ComposeSessionCol.Id, ids).
|
||||
Where(entity.ComposeSessionCol.Creator, creator).
|
||||
Where(entity.ComposeSessionCol.SessionId, sessionId).
|
||||
All()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = r.Structs(&list)
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteByIds 批量删除编排会话
|
||||
func (d *composeSessionDao) DeleteByIds(ctx context.Context, ids []int64, creator, sessionId string) (int64, error) {
|
||||
if len(ids) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
r, err := gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameComposeSession).
|
||||
WhereIn(entity.ComposeSessionCol.Id, ids).
|
||||
Where(entity.ComposeSessionCol.Creator, creator).
|
||||
Where(entity.ComposeSessionCol.SessionId, sessionId).
|
||||
Delete()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return r.RowsAffected()
|
||||
}
|
||||
55
dao/compose_task_dao.go
Normal file
55
dao/compose_task_dao.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"prompts-core/consts/public"
|
||||
"prompts-core/model/entity"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var ComposeTask = &composeTaskDao{}
|
||||
|
||||
type composeTaskDao struct{}
|
||||
|
||||
// Insert 插入
|
||||
func (d *composeTaskDao) Insert(ctx context.Context, req *entity.ComposeTask) (id int64, err error) {
|
||||
var m = new(entity.ComposeTask)
|
||||
err = gconv.Struct(req, &m)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r, err := gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameComposeTask).
|
||||
Insert(m)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
// Get 获取
|
||||
func (d *composeTaskDao) Get(ctx context.Context, req *entity.ComposeTask, fields ...string) (m *entity.ComposeTask, err error) {
|
||||
r, err := gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameComposeTask).
|
||||
OmitEmpty().
|
||||
Where(entity.ComposeTaskCol.TaskId, req.TaskId).
|
||||
Fields(fields).One()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = r.Struct(&m)
|
||||
return
|
||||
}
|
||||
|
||||
// Update 更新
|
||||
func (d *composeTaskDao) Update(ctx context.Context, req *entity.ComposeTask) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameComposeTask).
|
||||
OmitEmpty().
|
||||
Data(&req).
|
||||
Where(entity.ComposeTaskCol.TaskId, req.TaskId).
|
||||
Update()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.RowsAffected()
|
||||
}
|
||||
98
dao/provider_protocol_dao.go
Normal file
98
dao/provider_protocol_dao.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"context"
|
||||
"prompts-core/consts/public"
|
||||
"prompts-core/model/entity"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var ProviderProtocol = &providerProtocolDao{}
|
||||
|
||||
type providerProtocolDao struct{}
|
||||
|
||||
// Insert 新增协议配置
|
||||
func (d *providerProtocolDao) Insert(ctx context.Context, req *entity.ProviderProtocol) (id int64, err error) {
|
||||
var m = new(entity.ProviderProtocol)
|
||||
err = gconv.Struct(req, &m)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
r, err := gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameProviderProtocol).
|
||||
Insert(m)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
// Get 查询协议配置
|
||||
func (d *providerProtocolDao) Get(ctx context.Context, req *entity.ProviderProtocol, fields ...string) (res *entity.ProviderProtocol, err error) {
|
||||
r, err := gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameProviderProtocol).
|
||||
NoTenantId(ctx).
|
||||
OmitEmpty().
|
||||
Where(entity.ProviderProtocolCol.Id, req.Id).
|
||||
Where(entity.ProviderProtocolCol.ProviderName, req.ProviderName). //主要是根据运营商查询
|
||||
Where(entity.ProviderProtocolCol.Status, 1).
|
||||
Fields(fields).One()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.IsEmpty() {
|
||||
return nil, nil
|
||||
}
|
||||
err = r.Struct(&res)
|
||||
return
|
||||
}
|
||||
|
||||
// List 列表查询
|
||||
func (d *providerProtocolDao) List(ctx context.Context, req *entity.ProviderProtocol, page, size int, fields ...string) (list []*entity.ProviderProtocol, total int, err error) {
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if size <= 0 {
|
||||
size = 10
|
||||
}
|
||||
model := gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameProviderProtocol).
|
||||
Fields(fields).
|
||||
OmitEmpty()
|
||||
model.Where(entity.ProviderProtocolCol.ProviderName, req.ProviderName)
|
||||
model.Where(entity.ProviderProtocolCol.Status, req.Status)
|
||||
model.OrderDesc(entity.ProviderProtocolCol.CreatedAt)
|
||||
model.Page(page, size)
|
||||
r, total, err := model.AllAndCount(false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = r.Structs(&list)
|
||||
return
|
||||
}
|
||||
|
||||
// Update 更新协议配置
|
||||
func (d *providerProtocolDao) Update(ctx context.Context, req *entity.ProviderProtocol) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameProviderProtocol).
|
||||
OmitEmpty().
|
||||
Where(entity.ProviderProtocolCol.Id, req.Id).
|
||||
Data(req).
|
||||
Update()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return r.RowsAffected()
|
||||
}
|
||||
|
||||
// Delete 软删除协议配置
|
||||
func (d *providerProtocolDao) Delete(ctx context.Context, id int64) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx, public.DbNameModelGateway).Model(ctx, public.TableNameProviderProtocol).
|
||||
Where(entity.ProviderProtocolCol.Id, id).
|
||||
Data(map[string]any{
|
||||
entity.ProviderProtocolCol.DeletedAt: "NOW()",
|
||||
}).
|
||||
Update()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return r.RowsAffected()
|
||||
}
|
||||
89
go.mod
Normal file
89
go.mod
Normal file
@@ -0,0 +1,89 @@
|
||||
module prompts-core
|
||||
|
||||
go 1.26.1
|
||||
|
||||
require (
|
||||
gitea.redpowerfuture.com/red-future/common v0.0.23
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.2
|
||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.2
|
||||
github.com/gogf/gf/v2 v2.10.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||
github.com/armon/go-metrics v0.4.1 // indirect
|
||||
github.com/bwmarrin/snowflake v0.3.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/clbanning/mxj/v2 v2.7.0 // indirect
|
||||
github.com/dgraph-io/badger/v4 v4.2.0 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/emirpasic/gods/v2 v2.0.0-alpha // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/go-ego/gse v1.0.2 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/gogf/gf/contrib/registry/consul/v2 v2.9.5 // indirect
|
||||
github.com/gogf/gf/contrib/trace/otlphttp/v2 v2.9.5 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
|
||||
github.com/golang/glog v1.2.5 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v1.0.0 // indirect
|
||||
github.com/google/flatbuffers v1.12.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
|
||||
github.com/grokify/html-strip-tags-go v0.1.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
|
||||
github.com/hashicorp/consul/api v1.26.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-hclog v1.5.0 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/hashicorp/serf v0.10.1 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/magiconair/properties v1.8.10 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/olekukonko/errors v1.1.0 // indirect
|
||||
github.com/olekukonko/ll v0.0.9 // indirect
|
||||
github.com/olekukonko/tablewriter v1.1.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/r3labs/diff/v2 v2.15.1 // indirect
|
||||
github.com/redis/go-redis/v9 v9.12.1 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/tiger1103/gfast-token v1.0.10 // indirect
|
||||
github.com/vcaesar/cedar v0.30.0 // indirect
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
||||
go.mongodb.org/mongo-driver/v2 v2.4.0 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
|
||||
google.golang.org/grpc v1.75.0 // indirect
|
||||
google.golang.org/protobuf v1.36.8 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
461
go.sum
Normal file
461
go.sum
Normal file
@@ -0,0 +1,461 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
gitea.redpowerfuture.com/red-future/common v0.0.23 h1:xieoA00iKOCDm5SO9iXn+cSyMKBAlZwI0fuEVPWrHLg=
|
||||
gitea.redpowerfuture.com/red-future/common v0.0.23/go.mod h1:50U1Xi+Ie56z09S5LQbZvaken0Mxv3OeS9LgR7U/ZRY=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=
|
||||
github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=
|
||||
github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
|
||||
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgraph-io/badger/v4 v4.2.0 h1:kJrlajbXXL9DFTNuhhu9yCx7JJa4qpYWxtE8BzuWsEs=
|
||||
github.com/dgraph-io/badger/v4 v4.2.0/go.mod h1:qfCqhPoWDFJRx1gp5QwwyGo8xk1lbHUxvK9nK0OGAak=
|
||||
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
|
||||
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/emirpasic/gods/v2 v2.0.0-alpha h1:dwFlh8pBg1VMOXWGipNMRt8v96dKAIvBehtCt6OtunU=
|
||||
github.com/emirpasic/gods/v2 v2.0.0-alpha/go.mod h1:W0y4M2dtBB9U5z3YlghmpuUhiaZT2h6yoeE+C1sCp6A=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/go-ego/gse v1.0.2 h1:+27lYFPhQEhA9igtdOsJPRKYL/k3TwYsxBF5jr6KFv4=
|
||||
github.com/go-ego/gse v1.0.2/go.mod h1:Fy35G+q7VV7Et1zIKO8o/sW1kkugV3znXap/lF/11zc=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.2 h1:u8EpP24GkprogROnJ7htMov9Fc66pTP1eVYrWxiCYOs=
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.2/go.mod h1:GmvM3r8GVByVMi4RD2+MCs5+CfxVXPMeT8mVDkAaAXE=
|
||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.2 h1:iTQegT+lEg/wDKvj2mi3W1wrdrwFarjokf88EXVVgu4=
|
||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.2/go.mod h1:ZRw3GNz5cq4uYrW4TPSVyrYWaoqzujKdWro/AOcGBaE=
|
||||
github.com/gogf/gf/contrib/registry/consul/v2 v2.9.5 h1:eUqwJ/qNH8lJ6yssiqskazgp1ACQuNU6zXlLOZVuXTQ=
|
||||
github.com/gogf/gf/contrib/registry/consul/v2 v2.9.5/go.mod h1:sjQyMry9+0POYZCA6lHXBxO77WoNKkruJpRB4xKqk5k=
|
||||
github.com/gogf/gf/contrib/trace/otlphttp/v2 v2.9.5 h1:tHUEZYB5GTqEYYVDYnlGobf1xISARKDE4KHVlgjwTec=
|
||||
github.com/gogf/gf/contrib/trace/otlphttp/v2 v2.9.5/go.mod h1:cfzTn2HS9RDX8f5pUVkbGxUWcSosouqfNQ1G6cY0V88=
|
||||
github.com/gogf/gf/v2 v2.10.2 h1:46IO0Uc8e85/FqdftJFskfDejJLBL0JBnGS5qOftUu8=
|
||||
github.com/gogf/gf/v2 v2.10.2/go.mod h1:Svl1N+E8G/QshU2DUbh/3J/AJauqCgUnxHurXWR4Qx0=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I=
|
||||
github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
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.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/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
||||
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw=
|
||||
github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
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.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||
github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4=
|
||||
github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
|
||||
github.com/hashicorp/consul/api v1.26.1 h1:5oSXOO5fboPZeW5SN+TdGFP/BILDgBm19OrPZ/pICIM=
|
||||
github.com/hashicorp/consul/api v1.26.1/go.mod h1:B4sQTeaSO16NtynqrAdwOlahJ7IUDZM9cj2420xYL8A=
|
||||
github.com/hashicorp/consul/sdk v0.15.0 h1:2qK9nDrr4tiJKRoxPGhm6B7xJjLVIQqkjiab2M4aKjU=
|
||||
github.com/hashicorp/consul/sdk v0.15.0/go.mod h1:r/OmRRPbHOe0yxNahLw7G9x5WG17E1BIECMtCjcPSNo=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
|
||||
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
|
||||
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=
|
||||
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
|
||||
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
|
||||
github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM=
|
||||
github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=
|
||||
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
|
||||
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
|
||||
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
||||
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
|
||||
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
|
||||
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
||||
github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI=
|
||||
github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g=
|
||||
github.com/olekukonko/tablewriter v1.1.0 h1:N0LHrshF4T39KvI96fn6GT8HEjXRXYNDrDjKFDB7RIY=
|
||||
github.com/olekukonko/tablewriter v1.1.0/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg=
|
||||
github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc=
|
||||
github.com/redis/go-redis/v9 v9.12.1 h1:k5iquqv27aBtnTm2tIkROUDp8JBXhXZIVu1InSgvovg=
|
||||
github.com/redis/go-redis/v9 v9.12.1/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
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.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tiger1103/gfast-token v1.0.10 h1:fNiBE/Dq5iTHvTGlCx3DmXa2o4hr0NtumFpffZ39k6s=
|
||||
github.com/tiger1103/gfast-token v1.0.10/go.mod h1:a/21mxmj7zFeNvjhZSC0XpEAFHfb1aT2k6DXnufFU1s=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/vcaesar/cedar v0.30.0 h1:9fSDpM7FTjjUdPiBUUa0MWYMRGSEcqgFXvppZcZ4d7Y=
|
||||
github.com/vcaesar/cedar v0.30.0/go.mod h1:lyuGvALuZZDPNXwpzv/9LyxW+8Y6faN7zauFezNsnik=
|
||||
github.com/vcaesar/tt v0.20.1 h1:D/jUeeVCNbq3ad8M7hhtB3J9x5RZ6I1n1eZ0BJp7M+4=
|
||||
github.com/vcaesar/tt v0.20.1/go.mod h1:cH2+AwGAJm19Wa6xvEa+0r+sXDJBT0QgNQey6mwqLeU=
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.mongodb.org/mongo-driver/v2 v2.4.0 h1:Oq6BmUAAFTzMeh6AonuDlgZMuAuEiUxoAD1koK5MuFo=
|
||||
go.mongodb.org/mongo-driver/v2 v2.4.0/go.mod h1:jHeEDJHJq7tm6ZF45Issun9dbogjfnPySb1vXA7EeAI=
|
||||
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
|
||||
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
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.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
||||
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
36
main.go
Normal file
36
main.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/signal"
|
||||
"prompts-core/controller"
|
||||
"syscall"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/http"
|
||||
"gitea.redpowerfuture.com/red-future/common/jaeger"
|
||||
_ "gitea.redpowerfuture.com/red-future/common/swagger"
|
||||
_ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
|
||||
_ "github.com/gogf/gf/contrib/nosql/redis/v2"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
defer jaeger.ShutDown(ctx)
|
||||
// 注册路由
|
||||
http.RouteRegister([]interface{}{
|
||||
controller.Prompt,
|
||||
controller.Session,
|
||||
})
|
||||
|
||||
// 监听退出信号,确保 Ctrl+C 能完整退出并关闭 gateway server
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
|
||||
<-quit
|
||||
|
||||
g.Log().Infof(ctx, "[main] 收到退出信号,开始优雅退出...")
|
||||
cancel()
|
||||
_ = http.Httpserver.Shutdown()
|
||||
}
|
||||
53
model/dto/prompt_compose_dto.go
Normal file
53
model/dto/prompt_compose_dto.go
Normal file
@@ -0,0 +1,53 @@
|
||||
package dto
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
type ComposeMessagesReq struct {
|
||||
g.Meta `path:"/composeMessages" method:"post" tags:"提示词处理" summary:"拼接提示词" dc:"按 modelTypeId 读取 prompts_model_prompt.prompt_info 与 response_json_schema;form 作为系统表单,userForm 作为用户表单,结合 userFiles 调用 model-gateway,并直接返回最终 messages"`
|
||||
ModelName string `p:"modelName" json:"modelName" v:"required#modelName不能为空" dc:"实际请求的网关模型名称"`
|
||||
BuildType int `p:"buildType" json:"buildType" v:"required#buildType不能为空" dc:"构建类型"` //判断节点
|
||||
NodeId string `p:"nodeId" json:"nodeId" dc:"节点ID"`
|
||||
SessionId string `p:"sessionId" json:"sessionId" dc:"会话ID"` //v:"required#sessionId不能为空"
|
||||
Cause string `p:"cause" json:"cause" v:"required-if:IsBuilder,false#原因不能为空" dc:"原因"`
|
||||
CallbackUrl string `p:"callbackUrl" json:"callbackUrl" dc:"回调地址"`
|
||||
Form []map[string]any `p:"form" json:"form" dc:"系统表单:form 下所有字段都作为系统提示词来源"`
|
||||
UserForm []map[string]any `p:"userForm" json:"userForm" dc:"用户表单:userForm 下所有字段都作为用户提示词来源;若与 form 含义接近则严格覆盖系统字段"`
|
||||
Consult []ConsultItem `json:"consult" dc:"附件列表(图片/视频/音频)"`
|
||||
SkillName string `p:"skillName" json:"skillName" dc:"技能名称"`
|
||||
}
|
||||
|
||||
// ConsultItem 单个附件
|
||||
type ConsultItem struct {
|
||||
Type string `json:"type" dc:"附件类型:image/video/audio"`
|
||||
Url string `json:"url" dc:"附件地址"`
|
||||
}
|
||||
type ComposeMessagesRes struct {
|
||||
TaskId string `json:"taskId" dc:"任务ID"`
|
||||
}
|
||||
|
||||
type CallbackReq struct {
|
||||
g.Meta `path:"/callback" method:"post" tags:"提示词处理" summary:"model-gateway 回调" dc:"model-gateway 成功后 POST 回调:callbackUrl/{bizName}"`
|
||||
TaskId string `json:"task_id" v:"required#task_id不能为空" dc:"网关任务ID"`
|
||||
State int `json:"state" dc:"网关任务状态"`
|
||||
OssFile string `json:"oss_file" dc:"结果文件地址"`
|
||||
FileType string `json:"file_type" dc:"结果文件类型"`
|
||||
ErrorMsg string `json:"error_msg" dc:"错误信息"`
|
||||
}
|
||||
|
||||
type CallbackRes struct {
|
||||
}
|
||||
|
||||
type GetComposeTaskReq struct {
|
||||
g.Meta `path:"/getComposeTask" method:"get" tags:"提示词处理" summary:"查询拼接任务" dc:"按 taskId 查询提示词拼接任务结果"`
|
||||
TaskId string `p:"taskId" json:"taskId" v:"required#taskId不能为空" dc:"任务ID"`
|
||||
}
|
||||
|
||||
type GetComposeTaskRes struct {
|
||||
TaskId string `json:"taskId" dc:"任务ID"`
|
||||
Status string `json:"status" dc:"业务状态"`
|
||||
GatewayState int `json:"gatewayState" dc:"网关状态"`
|
||||
ErrorMessage string `json:"errorMessage" dc:"错误信息"`
|
||||
Messages map[string]any `json:"messages" dc:"最终消息数组"`
|
||||
OssFile string `json:"ossFile" dc:"结果文件地址"`
|
||||
FileType string `json:"fileType" dc:"结果文件类型"`
|
||||
}
|
||||
80
model/dto/prompt_session_dto.go
Normal file
80
model/dto/prompt_session_dto.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package dto
|
||||
|
||||
import "github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
// HistoryRound 一轮对话
|
||||
type HistoryRound struct {
|
||||
Id int64 `json:"id" dc:"记录ID"`
|
||||
SessionId string `json:"sessionId" dc:"会话ID"`
|
||||
NodeId string `json:"nodeId" dc:"节点ID"`
|
||||
User map[string]any `json:"user" dc:"用户消息"`
|
||||
Assistant map[string]any `json:"assistant" dc:"助手回复"`
|
||||
CreatedAt string `json:"createdAt" dc:"创建时间"`
|
||||
UpdatedAt string `json:"updatedAt" dc:"更新时间"`
|
||||
}
|
||||
|
||||
// SessionCallbackReq 会话回调请求
|
||||
type SessionCallbackReq struct {
|
||||
g.Meta `path:"/callback" method:"post" tags:"会话管理" summary:"会话回调"`
|
||||
Messages map[string]any `json:"messages" v:"required" dc:"消息数组"`
|
||||
EpicycleId int64 `json:"epicycleId" v:"required" dc:"轮次ID"`
|
||||
}
|
||||
|
||||
// SessionCallbackRes 会话回调响应
|
||||
type SessionCallbackRes struct {
|
||||
Status bool `json:"status" dc:"状态"`
|
||||
SessionId string `json:"sessionId" dc:"会话ID"`
|
||||
}
|
||||
|
||||
// GetHistoryListReq 获取历史列表请求(前端)
|
||||
type GetHistoryListReq struct {
|
||||
g.Meta `path:"/historyList" method:"get" tags:"会话管理" summary:"获取历史列表"`
|
||||
Page int `json:"page" d:"1" dc:"页码"`
|
||||
Size int `json:"size" d:"10" dc:"每页条数"`
|
||||
}
|
||||
|
||||
// GetHistoryListRes 获取历史列表响应
|
||||
type GetHistoryListRes struct {
|
||||
List []HistoryRound `json:"list" dc:"历史列表"`
|
||||
Total int `json:"total" dc:"总数"`
|
||||
}
|
||||
|
||||
// GetHistoryMessagesReq 获取历史消息请求(提示词拼接)
|
||||
type GetHistoryMessagesReq struct {
|
||||
g.Meta `path:"/historyMessages" method:"get" tags:"会话管理" summary:"获取历史消息"`
|
||||
SessionId string `json:"sessionId" v:"required" dc:"会话ID"`
|
||||
NodeId string `json:"nodeId" dc:"节点ID"`
|
||||
}
|
||||
|
||||
// GetHistoryMessagesRes 获取历史消息响应
|
||||
type GetHistoryMessagesRes struct {
|
||||
Messages []FlatMessage `json:"messages"`
|
||||
}
|
||||
|
||||
type FlatMessage struct {
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
// DeleteMessagesReq 批量删除消息请求
|
||||
type DeleteMessagesReq struct {
|
||||
g.Meta `path:"/deleteMessages" method:"post" tags:"会话管理" summary:"批量删除消息"`
|
||||
SessionId string `json:"sessionId" v:"required" dc:"会话ID"`
|
||||
MsgIds []int64 `json:"msgIds" v:"required" dc:"消息ID列表"`
|
||||
}
|
||||
|
||||
// DeleteMessagesRes 批量删除消息响应
|
||||
type DeleteMessagesRes struct {
|
||||
Ok bool `json:"ok" dc:"是否成功"`
|
||||
}
|
||||
|
||||
// DeleteSessionReq 删除整个会话请求
|
||||
type DeleteSessionReq struct {
|
||||
g.Meta `path:"/deleteSession" method:"post" tags:"会话管理" summary:"删除整个会话"`
|
||||
SessionId string `json:"sessionId" v:"required" dc:"会话ID"`
|
||||
}
|
||||
|
||||
// DeleteSessionRes 删除整个会话响应
|
||||
type DeleteSessionRes struct {
|
||||
Ok bool `json:"ok" dc:"是否成功"`
|
||||
}
|
||||
30
model/entity/prompts_compose_session.go
Normal file
30
model/entity/prompts_compose_session.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package entity
|
||||
|
||||
import "gitea.redpowerfuture.com/red-future/common/beans"
|
||||
|
||||
type ComposeSession struct {
|
||||
beans.SQLBaseDO `orm:",inline"`
|
||||
SessionId string `orm:"session_id" json:"sessionId"`
|
||||
NodeId string `orm:"node_id" json:"nodeId"`
|
||||
RequestContent map[string]any `orm:"request_content" json:"requestContent"`
|
||||
ResponseContent map[string]any `orm:"response_content" json:"responseContent"`
|
||||
Remark string `orm:"remark" json:"remark"`
|
||||
}
|
||||
|
||||
type composeSessionCol struct {
|
||||
beans.SQLBaseCol
|
||||
SessionId string
|
||||
NodeId string
|
||||
RequestContent string
|
||||
ResponseContent string
|
||||
Remark string
|
||||
}
|
||||
|
||||
var ComposeSessionCol = composeSessionCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
SessionId: "session_id",
|
||||
NodeId: "node_id",
|
||||
RequestContent: "request_content",
|
||||
ResponseContent: "response_content",
|
||||
Remark: "remark",
|
||||
}
|
||||
51
model/entity/prompts_compose_task.go
Normal file
51
model/entity/prompts_compose_task.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package entity
|
||||
|
||||
import "gitea.redpowerfuture.com/red-future/common/beans"
|
||||
|
||||
type ComposeTask struct {
|
||||
beans.SQLBaseDO `orm:",inline"`
|
||||
TaskId string `orm:"task_id" json:"taskId"`
|
||||
ModelName string `orm:"model_name" json:"modelName"`
|
||||
SkillName string `orm:"skill_name" json:"skillName"`
|
||||
BuildType int `orm:"build_type" json:"buildType"`
|
||||
CallbackUrl string `orm:"callback_url" json:"callbackUrl"`
|
||||
GatewayState int `orm:"gateway_state" json:"gatewayState"`
|
||||
RequestPayload map[string]any `orm:"request_payload" json:"requestPayload"`
|
||||
ResultJson map[string]any `orm:"result_json" json:"resultJson"`
|
||||
Status string `orm:"status" json:"status"`
|
||||
ErrorMessage string `orm:"error_message" json:"errorMessage"`
|
||||
OssFile string `orm:"oss_file" json:"ossFile"`
|
||||
FileType string `orm:"file_type" json:"fileType"`
|
||||
}
|
||||
|
||||
type composeTaskCol struct {
|
||||
beans.SQLBaseCol
|
||||
TaskId string
|
||||
ModelName string
|
||||
SkillName string
|
||||
BuildType string
|
||||
CallbackUrl string
|
||||
GatewayState string
|
||||
RequestPayload string
|
||||
ResultJson string
|
||||
Status string
|
||||
ErrorMessage string
|
||||
OssFile string
|
||||
FileType string
|
||||
}
|
||||
|
||||
var ComposeTaskCol = composeTaskCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
TaskId: "task_id",
|
||||
ModelName: "model_name",
|
||||
SkillName: "skill_name",
|
||||
BuildType: "build_type",
|
||||
CallbackUrl: "callback_url",
|
||||
GatewayState: "gateway_state",
|
||||
RequestPayload: "request_payload",
|
||||
ResultJson: "result_json",
|
||||
Status: "status",
|
||||
ErrorMessage: "error_message",
|
||||
OssFile: "oss_file",
|
||||
FileType: "file_type",
|
||||
}
|
||||
49
model/entity/prompts_provider_protocol.go
Normal file
49
model/entity/prompts_provider_protocol.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package entity
|
||||
|
||||
import "gitea.redpowerfuture.com/red-future/common/beans"
|
||||
|
||||
// ProviderProtocol 模型协议映射配置
|
||||
type ProviderProtocol struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
// 业务字段
|
||||
ProviderName string `orm:"provider_name" json:"providerName"`
|
||||
TargetField string `orm:"target_field" json:"targetField"`
|
||||
MergeOrder []string `orm:"merge_order" json:"mergeOrder"`
|
||||
RoleMapping map[string]any `orm:"role_mapping" json:"roleMapping"`
|
||||
ContentMapping map[string]any `orm:"content_mapping" json:"contentMapping"`
|
||||
Capabilities map[string]any `orm:"capabilities" json:"capabilities"`
|
||||
RequestTemplate map[string]any `orm:"request_template" json:"requestTemplate"`
|
||||
SystemPromptTemplate string `orm:"system_prompt_template" json:"systemPromptTemplate"`
|
||||
Status int `orm:"status" json:"status"`
|
||||
Remark string `orm:"remark" json:"remark"`
|
||||
}
|
||||
|
||||
// providerProtocolCol 列名
|
||||
type providerProtocolCol struct {
|
||||
beans.SQLBaseCol
|
||||
ProviderName string
|
||||
TargetField string
|
||||
MergeOrder string
|
||||
RoleMapping string
|
||||
ContentMapping string
|
||||
Capabilities string
|
||||
RequestTemplate string
|
||||
SystemPromptTemplate string
|
||||
Status string
|
||||
Remark string
|
||||
}
|
||||
|
||||
// ProviderProtocolCol 列名常量
|
||||
var ProviderProtocolCol = providerProtocolCol{
|
||||
SQLBaseCol: beans.DefSQLBaseCol,
|
||||
ProviderName: "provider_name",
|
||||
TargetField: "target_field",
|
||||
MergeOrder: "merge_order",
|
||||
RoleMapping: "role_mapping",
|
||||
ContentMapping: "content_mapping",
|
||||
Capabilities: "capabilities",
|
||||
RequestTemplate: "request_template",
|
||||
SystemPromptTemplate: "system_prompt_template",
|
||||
Status: "status",
|
||||
Remark: "remark",
|
||||
}
|
||||
195
service/gateway/gateway_http_service.go
Normal file
195
service/gateway/gateway_http_service.go
Normal file
@@ -0,0 +1,195 @@
|
||||
package gateway
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"prompts-core/common/util"
|
||||
"prompts-core/model/entity"
|
||||
"strings"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
commonHttp "gitea.redpowerfuture.com/red-future/common/http"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// CreateTaskReq 创建任务请求
|
||||
type CreateTaskReq struct {
|
||||
TaskId string `json:"task_id"`
|
||||
State int `json:"state"`
|
||||
OssFile string `json:"oss_file"`
|
||||
FileType string `json:"file_type"`
|
||||
Text string `json:"text"`
|
||||
ErrorMsg string `json:"error_msg"`
|
||||
}
|
||||
|
||||
// CreateGatewayTask 创建网关异步任务
|
||||
func CreateGatewayTask(ctx context.Context, payload map[string]any) (string, error) {
|
||||
fullURL := "model-gateway/task/createTask"
|
||||
headers := util.ForwardHeaders(ctx)
|
||||
var req CreateTaskReq
|
||||
body, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := commonHttp.Post(ctx, fullURL, headers, &req, body); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return req.TaskId, nil
|
||||
}
|
||||
|
||||
type GetModelConfigResp struct {
|
||||
Model *AsynchModel `json:"model"`
|
||||
}
|
||||
|
||||
type AsynchModel struct {
|
||||
beans.SQLBaseDO `orm:",inline"`
|
||||
ModelName string `orm:"model_name" json:"modelName"`
|
||||
ModelType int `orm:"model_type" json:"modelType"`
|
||||
BaseURL string `orm:"base_url" json:"baseUrl"`
|
||||
HttpMethod string `orm:"http_method" json:"httpMethod"`
|
||||
HeadMsg map[string]any `orm:"head_msg" json:"headMsg"`
|
||||
Form []map[string]any `orm:"form_json" json:"form"`
|
||||
RequestMapping map[string]any `orm:"request_mapping" json:"requestMapping"`
|
||||
ResponseMapping map[string]any `orm:"response_mapping" json:"responseMapping"`
|
||||
ResponseBody string `orm:"response_body" json:"responseBody"`
|
||||
ResponseTokenField string `orm:"response_token_field" json:"responseTokenField"`
|
||||
IsPrivate *int `orm:"is_private" json:"isPrivate"`
|
||||
IsChatModel int `orm:"is_chat_model" json:"isChatModel"`
|
||||
CallModel int `orm:"call_model" json:"callModel"`
|
||||
ApiKey string `orm:"api_key" json:"apiKey"`
|
||||
Enabled *int `orm:"enabled" json:"enabled"`
|
||||
MaxConcurrency int `orm:"max_concurrency" json:"maxConcurrency"`
|
||||
TimeoutSeconds int `orm:"timeout_seconds" json:"timeoutSeconds"`
|
||||
RetryTimes int `orm:"retry_times" json:"retryTimes"`
|
||||
AutoCleanSeconds int `orm:"auto_clean_seconds" json:"autoCleanSeconds"`
|
||||
IsOwner *int `json:"isOwner" orm:"is_owner"`
|
||||
OperatorName string `orm:"operator_name" json:"operatorName"`
|
||||
TokenConfig map[string]any `orm:"token_config" json:"tokenConfig"`
|
||||
ExtendMapping map[string]any `orm:"extend_mapping" json:"extendMapping"`
|
||||
QueryConfig map[string]any `orm:"query_config" json:"queryConfig"`
|
||||
StreamConfig map[string]any `orm:"stream_config" json:"streamConfig"`
|
||||
FirstFrame string `orm:"first_frame" json:"firstFrame"`
|
||||
LastFrame string `orm:"last_frame" json:"lastFrame"`
|
||||
CallbackUrl string `orm:"callback_url" json:"callbackUrl"`
|
||||
}
|
||||
|
||||
// GetModelConfig 获取模型配置
|
||||
func GetModelConfig(ctx context.Context, req *AsynchModel) (model *AsynchModel, err error) {
|
||||
fullURL := "model-gateway/model/getModel"
|
||||
// 拼接 query 参数
|
||||
var params []string
|
||||
if req.Creator != "" {
|
||||
params = append(params, fmt.Sprintf("creator=%s", req.Creator))
|
||||
}
|
||||
if req.ModelName != "" {
|
||||
params = append(params, fmt.Sprintf("modelName=%s", req.ModelName))
|
||||
}
|
||||
if req.IsChatModel != 0 {
|
||||
params = append(params, fmt.Sprintf("isChatModel=%d", req.IsChatModel))
|
||||
}
|
||||
if len(params) > 0 {
|
||||
fullURL += "?" + strings.Join(params, "&")
|
||||
}
|
||||
headers := util.ForwardHeaders(ctx)
|
||||
var resp GetModelConfigResp
|
||||
if err = commonHttp.Get(ctx, fullURL, headers, &resp, nil); err != nil {
|
||||
return nil, fmt.Errorf("获取模型配置失败: %w", err)
|
||||
}
|
||||
if resp.Model == nil {
|
||||
return nil, fmt.Errorf("模型不存在")
|
||||
}
|
||||
return resp.Model, nil
|
||||
}
|
||||
|
||||
// GetTaskResultRes 任务结果响应
|
||||
type GetTaskResultRes struct {
|
||||
OssFile string `json:"ossFile" dc:"结果文件OSS地址"`
|
||||
State int `json:"state" dc:"任务状态"`
|
||||
}
|
||||
|
||||
// QueryGatewayTaskState 查询网关任务状态
|
||||
func QueryGatewayTaskState(ctx context.Context, taskID string) (int, error) {
|
||||
fullURL := fmt.Sprintf("model-gateway/task/getTaskResult?taskId=%s", taskID)
|
||||
headers := util.ForwardHeaders(ctx)
|
||||
var req GetTaskResultRes
|
||||
if err := commonHttp.Get(ctx, fullURL, headers, &req, nil); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return req.State, nil
|
||||
}
|
||||
|
||||
// SkillUserVO 技能用户视图对象
|
||||
type SkillUserVO struct {
|
||||
Id int64 `json:"id,string"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
FileName string `json:"fileName"`
|
||||
FileUrl string `json:"fileUrl"`
|
||||
CreatedAt *gtime.Time `json:"createdAt"`
|
||||
UpdatedAt *gtime.Time `json:"updatedAt"`
|
||||
ImgAddressPrefix string `json:"imgAddressPrefix"`
|
||||
}
|
||||
|
||||
// GetSkillUser 获取技能用户信息
|
||||
func GetSkillUser(ctx context.Context, name string) (*SkillUserVO, error) {
|
||||
fullURL := fmt.Sprintf("ai-agent/skill/user/getUserOrTemplate?name=%s", name)
|
||||
headers := util.ForwardHeaders(ctx)
|
||||
var resp SkillUserVO
|
||||
var req struct{}
|
||||
if err := commonHttp.Get(ctx, fullURL, headers, &resp, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
// SendCallbackReq 发送回调的请求体
|
||||
type SendCallbackReq struct {
|
||||
TaskId string `json:"taskId"`
|
||||
Status string `json:"status"`
|
||||
EpicycleId int64 `json:"epicycleId"`
|
||||
ErrorMsg string `json:"errorMsg,omitempty"`
|
||||
}
|
||||
|
||||
// SendCallback 向业务方发送回调
|
||||
func SendCallback(ctx context.Context, composeTask *entity.ComposeTask, epicycleId int64) error {
|
||||
// 1. 检查回调地址
|
||||
if composeTask.CallbackUrl == "" {
|
||||
return fmt.Errorf("回调地址为空,taskId=%s", composeTask.TaskId)
|
||||
}
|
||||
// 2. 构造请求体
|
||||
req := SendCallbackReq{
|
||||
TaskId: composeTask.TaskId,
|
||||
Status: composeTask.Status,
|
||||
ErrorMsg: composeTask.ErrorMessage,
|
||||
EpicycleId: epicycleId,
|
||||
}
|
||||
// 3. 发送 POST 请求
|
||||
headers := util.ForwardHeaders(ctx)
|
||||
var resp struct{}
|
||||
g.Log().Infof(ctx, "[回调业务] 开始发送 taskId=%s 回调地址=%s",
|
||||
composeTask.TaskId, composeTask.CallbackUrl)
|
||||
if err := commonHttp.Post(ctx, composeTask.CallbackUrl, headers, &resp, req); err != nil {
|
||||
return fmt.Errorf("[回调业务] 发送失败 taskId=%s url=%s err=%w", composeTask.TaskId, composeTask.CallbackUrl, err)
|
||||
}
|
||||
g.Log().Infof(ctx, "[回调业务] 发送成功 taskId=%s 回调地址=%s ", composeTask.TaskId, composeTask.CallbackUrl)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DownloadFile 从 OSS 下载文件内容
|
||||
func DownloadFile(ossURL string) ([]byte, error) {
|
||||
resp, err := http.Get(ossURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("下载OSS文件失败: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("下载OSS文件返回非200: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return io.ReadAll(resp.Body)
|
||||
}
|
||||
161
service/prompt/prompt_build_service.go
Normal file
161
service/prompt/prompt_build_service.go
Normal file
@@ -0,0 +1,161 @@
|
||||
package prompt
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"prompts-core/service/gateway"
|
||||
"strings"
|
||||
|
||||
"prompts-core/common/util"
|
||||
"prompts-core/dao"
|
||||
"prompts-core/model/dto"
|
||||
"prompts-core/model/entity"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/utils"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
)
|
||||
|
||||
// buildPromptTypeRequest 构建提示词类型请求(BuildType=1)
|
||||
func buildPromptTypeRequest(ctx context.Context, req *dto.ComposeMessagesReq, aiModel *gateway.AsynchModel, chatModel *gateway.AsynchModel, ir *IR) (map[string]any, error) {
|
||||
//1) 构建系统提示词
|
||||
systemPrompt := promptBuildWithRounds(ctx, chatModel, aiModel)
|
||||
ir.AddSystem(systemPrompt)
|
||||
userPrompt := buildUserPrompt(ctx, req, util.GetModelPrompt(ctx, aiModel.ModelType))
|
||||
ir.AddUser(userPrompt)
|
||||
//2) 检查整体内容是否超出窗口
|
||||
if !checkOverallContent(ir, aiModel) {
|
||||
availableWindow := util.GetAvailableWindow(aiModel.TokenConfig)
|
||||
return nil, fmt.Errorf("整体内容超出模型窗口大小限制(可用窗口=%d tokens),请精简后重试", availableWindow)
|
||||
}
|
||||
return compileToProviderRequest(ctx, ir, chatModel, req)
|
||||
}
|
||||
|
||||
// buildNodeTypeRequest 构建节点类型请求(BuildType=2)
|
||||
func buildNodeTypeRequest(ctx context.Context, req *dto.ComposeMessagesReq, chatModel *gateway.AsynchModel, ir *IR) (map[string]any, error) {
|
||||
ir.AddUser(NodeBuild(ctx, req))
|
||||
return compileToProviderRequest(ctx, ir, chatModel, req)
|
||||
}
|
||||
|
||||
// buildStructTypeRequest 构建结构体类型请求(BuildType=3)
|
||||
func buildStructTypeRequest(ctx context.Context, req *dto.ComposeMessagesReq, chatModel *gateway.AsynchModel, ir *IR) (map[string]any, error) {
|
||||
customPrompt := gjson.New(req.UserForm).Get("0.prompt").String()
|
||||
ir.AddSystem(customPrompt)
|
||||
ir.AddUser(buildUserPrompt(ctx, req, ""))
|
||||
return compileToProviderRequest(ctx, ir, chatModel, req, customPrompt)
|
||||
}
|
||||
|
||||
// compileToProviderRequest 编译为 Provider 请求
|
||||
func compileToProviderRequest(ctx context.Context, ir *IR, chatModel *gateway.AsynchModel, req *dto.ComposeMessagesReq, customPrompt ...string) (map[string]any, error) {
|
||||
protocol, err := GetProtocolByProvider(ctx, chatModel.OperatorName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if protocol == nil {
|
||||
return nil, fmt.Errorf("协议配置不存在或获取失败")
|
||||
}
|
||||
// 如果传了自定义提示词,替换掉协议模板
|
||||
if len(customPrompt) > 0 && customPrompt[0] != "" {
|
||||
protocol.SystemPromptTemplate = customPrompt[0] +
|
||||
"【核心铁律】" +
|
||||
"1.【技能内容skill相关】必须完整拼接到System提示词中,作为System提示词的组成部分,不得拆分到其他位置。"
|
||||
}
|
||||
providerReq, err := Compile(ir, protocol, chatModel)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("编译请求失败: %w", err)
|
||||
}
|
||||
return map[string]any{
|
||||
"modelName": chatModel.ModelName,
|
||||
"bizName": util.GetServerName(ctx),
|
||||
"callbackUrl": utils.GetCallbackURL(ctx, "/prompt/callback"),
|
||||
"requestPayload": providerReq,
|
||||
"buildType": req.BuildType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// promptBuildWithRounds 构建提示词
|
||||
func promptBuildWithRounds(ctx context.Context, chatModel *gateway.AsynchModel, aiModel *gateway.AsynchModel) string {
|
||||
providerProtocol, err := dao.ProviderProtocol.Get(ctx, &entity.ProviderProtocol{
|
||||
ProviderName: chatModel.OperatorName,
|
||||
Status: 1,
|
||||
})
|
||||
if err != nil || providerProtocol == nil {
|
||||
return ""
|
||||
}
|
||||
outputJSON := gjson.New(util.ReverseMap(aiModel.RequestMapping, map[string]any{})).MustToJsonIndentString()
|
||||
|
||||
return fmt.Sprintf(providerProtocol.SystemPromptTemplate,
|
||||
outputJSON, //【输出结构】 %s
|
||||
)
|
||||
}
|
||||
|
||||
// checkOverallContent 检查整体内容是否超出窗口
|
||||
func checkOverallContent(ir *IR, model *gateway.AsynchModel) bool {
|
||||
fullContent := ir.String()
|
||||
return util.CountToken(fullContent, model.TokenConfig)
|
||||
}
|
||||
|
||||
// buildUserPrompt 构建用户提示词
|
||||
func buildUserPrompt(ctx context.Context, req *dto.ComposeMessagesReq, prompt string) string {
|
||||
var b strings.Builder
|
||||
b.WriteString(fmt.Sprintf("目标模型:%s\n", req.ModelName))
|
||||
if prompt != "" {
|
||||
b.WriteString(fmt.Sprintf("系统提示词:%s\n", prompt))
|
||||
}
|
||||
if skills := SkillMdContent(ctx, req.SkillName); skills != "" {
|
||||
b.WriteString(fmt.Sprintf("技能内容:\n%s\n", skills))
|
||||
}
|
||||
if formText := buildUserFormText(req.Form); formText != "" {
|
||||
b.WriteString(fmt.Sprintf("系统参数:\n%s\n", formText))
|
||||
}
|
||||
if userFormText := buildUserFormText(req.UserForm); userFormText != "" {
|
||||
b.WriteString(fmt.Sprintf("用户需求:\n%s\n", userFormText))
|
||||
}
|
||||
if len(req.Consult) > 0 {
|
||||
b.WriteString(fmt.Sprintf("参考附件:%s\n", gjson.New(req.Consult).String()))
|
||||
}
|
||||
if fileTexts := ExtractFileTexts(ctx, req.Consult); fileTexts != "" {
|
||||
b.WriteString(fmt.Sprintf("附件内容:\n%s\n", fileTexts))
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func buildUserFormText(form []map[string]any) string {
|
||||
if len(form) == 0 {
|
||||
return ""
|
||||
}
|
||||
var builder strings.Builder
|
||||
for _, item := range form {
|
||||
for k, v := range item {
|
||||
builder.WriteString(fmt.Sprintf("%s:\n", k))
|
||||
switch val := v.(type) {
|
||||
case []any:
|
||||
for i, elem := range val {
|
||||
builder.WriteString(fmt.Sprintf(" %d. ", i+1))
|
||||
if m, ok := elem.(map[string]any); ok {
|
||||
for mk, mv := range m {
|
||||
builder.WriteString(fmt.Sprintf("%s:%v ", mk, mv))
|
||||
}
|
||||
} else {
|
||||
builder.WriteString(fmt.Sprint(elem))
|
||||
}
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
default:
|
||||
builder.WriteString(fmt.Sprintf(" %v\n", v))
|
||||
}
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(builder.String())
|
||||
}
|
||||
|
||||
// NodeBuild 节点构建
|
||||
func NodeBuild(ctx context.Context, req *dto.ComposeMessagesReq) string {
|
||||
promptTpl := util.GetBuildPrompt(ctx)
|
||||
if promptTpl == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf(promptTpl,
|
||||
gjson.New(req.Form).MustToJsonString(),
|
||||
gjson.New(req.UserForm).MustToJsonString(),
|
||||
)
|
||||
}
|
||||
331
service/prompt/prompt_compose_service.go
Normal file
331
service/prompt/prompt_compose_service.go
Normal file
@@ -0,0 +1,331 @@
|
||||
package prompt
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"prompts-core/service/session"
|
||||
|
||||
"prompts-core/common/util"
|
||||
"prompts-core/consts/public"
|
||||
"prompts-core/dao"
|
||||
"prompts-core/model/dto"
|
||||
"prompts-core/model/entity"
|
||||
"prompts-core/service/gateway"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
"gitea.redpowerfuture.com/red-future/common/utils"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// ComposeMessages 核心拼接提示词主流程
|
||||
func ComposeMessages(ctx context.Context, req *dto.ComposeMessagesReq) (*dto.ComposeMessagesRes, error) {
|
||||
// 1) 获取模型信息
|
||||
chatModel, aiModel, err := GetModelMessage(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 2) 校验用户表单
|
||||
if err = validateUserForm(req, aiModel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return handleBuild(ctx, req, chatModel, aiModel)
|
||||
}
|
||||
|
||||
// GetModelMessage 获取模型信息
|
||||
func GetModelMessage(ctx context.Context, req *dto.ComposeMessagesReq) (*gateway.AsynchModel, *gateway.AsynchModel, error) {
|
||||
userInfo, err := utils.GetUserInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("获取用户信息失败: %w", err)
|
||||
}
|
||||
chatModel, err := gateway.GetModelConfig(ctx, &gateway.AsynchModel{
|
||||
SQLBaseDO: beans.SQLBaseDO{Creator: userInfo.UserName},
|
||||
IsChatModel: 1,
|
||||
})
|
||||
if err != nil || chatModel == nil {
|
||||
return nil, nil, errors.New("当前没有对话模型,请添加")
|
||||
}
|
||||
|
||||
aiModel, err := gateway.GetModelConfig(ctx, &gateway.AsynchModel{
|
||||
SQLBaseDO: beans.SQLBaseDO{TenantId: userInfo.TenantId, Creator: userInfo.UserName},
|
||||
ModelName: req.ModelName,
|
||||
})
|
||||
if err != nil || aiModel == nil {
|
||||
return nil, nil, errors.New("需要构建的模型不存在")
|
||||
}
|
||||
|
||||
return chatModel, aiModel, nil
|
||||
}
|
||||
|
||||
// validateUserForm 校验用户表单
|
||||
func validateUserForm(req *dto.ComposeMessagesReq, model *gateway.AsynchModel) error {
|
||||
if len(req.UserForm) == 0 {
|
||||
return nil
|
||||
}
|
||||
isValid, exceedTokens, err := util.CheckUserFormWithinWindow(req.UserForm, model.TokenConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("校验用户表单失败: %w", err)
|
||||
}
|
||||
|
||||
if !isValid {
|
||||
availableWindow := util.GetAvailableWindow(model.TokenConfig)
|
||||
return fmt.Errorf("UserForm 内容超出窗口大小: 超出 %d tokens,可用窗口 %d tokens,请精简后重试",
|
||||
exceedTokens, availableWindow)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleBuild 通用构建处理
|
||||
func handleBuild(ctx context.Context, req *dto.ComposeMessagesReq, chatModel, aiModel *gateway.AsynchModel) (*dto.ComposeMessagesRes, error) {
|
||||
// 1) 处理表单分批
|
||||
processedReq, _, err := ProcessUserFormBatches(ctx, req, aiModel)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("处理用户表单分批失败: %w", err)
|
||||
}
|
||||
|
||||
// 2) 构建推理请求
|
||||
ir := NewPromptIR()
|
||||
var taskReq map[string]any
|
||||
switch req.BuildType {
|
||||
case public.BuildTypePrompt:
|
||||
taskReq, err = buildPromptTypeRequest(ctx, processedReq, aiModel, chatModel, ir)
|
||||
case public.BuildTypeNode:
|
||||
taskReq, err = buildNodeTypeRequest(ctx, req, chatModel, ir)
|
||||
case public.BuildTypeStruct:
|
||||
taskReq, err = buildStructTypeRequest(ctx, req, chatModel, ir)
|
||||
default:
|
||||
return nil, errors.New("不支持的构建类型")
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("构建推理请求失败: %w", err)
|
||||
}
|
||||
|
||||
// 3) 调用网关创建任务
|
||||
taskID, err := gateway.CreateGatewayTask(ctx, taskReq)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建网关任务失败: %w", err)
|
||||
}
|
||||
if taskID == "" {
|
||||
return nil, errors.New("网关未返回taskId")
|
||||
}
|
||||
|
||||
// 4) 保存任务记录
|
||||
if _, err = dao.ComposeTask.Insert(ctx, &entity.ComposeTask{
|
||||
TaskId: taskID,
|
||||
ModelName: req.ModelName,
|
||||
SkillName: req.SkillName,
|
||||
BuildType: req.BuildType,
|
||||
CallbackUrl: req.CallbackUrl,
|
||||
RequestPayload: gconv.Map(req),
|
||||
Status: public.ComposeStatusPending,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &dto.ComposeMessagesRes{TaskId: taskID}, nil
|
||||
}
|
||||
|
||||
// Callback 回调处理
|
||||
func Callback(ctx context.Context, req *dto.CallbackReq) error {
|
||||
g.Log().Infof(ctx, "[开始回调处理] taskId=%s state=%d", req.TaskId, req.State)
|
||||
|
||||
// 1) 查询任务
|
||||
composeTask, err := dao.ComposeTask.Get(ctx, &entity.ComposeTask{TaskId: req.TaskId})
|
||||
if err != nil {
|
||||
return fmt.Errorf("查询任务失败: %w", err)
|
||||
}
|
||||
|
||||
// 2) 读取 OSS 文件内容
|
||||
var ossContent []byte
|
||||
if req.OssFile != "" {
|
||||
ossContent, err = gateway.DownloadFile(req.OssFile)
|
||||
if err != nil {
|
||||
g.Log().Warningf(ctx, "[回调处理] 读取OSS失败 taskId=%s err=%v", req.TaskId, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 3) 解析 OSS 内容为消息
|
||||
var messages map[string]any
|
||||
if len(ossContent) > 0 {
|
||||
messages, _ = gjson.New(ossContent).Map(), nil
|
||||
}
|
||||
|
||||
// 4) 处理失败
|
||||
if req.State == 3 {
|
||||
return handleCallbackFailed(ctx, req, composeTask, messages)
|
||||
}
|
||||
|
||||
// 5) 处理成功
|
||||
if req.State == 2 {
|
||||
return handleCallbackSuccess(ctx, req, composeTask, messages)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleCallbackFailed 处理回调失败
|
||||
func handleCallbackFailed(ctx context.Context, req *dto.CallbackReq, composeTask *entity.ComposeTask, messages map[string]any) error {
|
||||
_, err := dao.ComposeTask.Update(ctx, &entity.ComposeTask{
|
||||
TaskId: req.TaskId,
|
||||
Status: public.ComposeStatusFailed,
|
||||
ErrorMessage: req.ErrorMsg,
|
||||
GatewayState: req.State,
|
||||
OssFile: req.OssFile,
|
||||
FileType: req.FileType,
|
||||
ResultJson: messages,
|
||||
})
|
||||
if composeTask.CallbackUrl != "" {
|
||||
composeTask.Status = public.ComposeStatusFailed
|
||||
composeTask.ErrorMessage = req.ErrorMsg
|
||||
_ = gateway.SendCallback(ctx, composeTask, 0)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// handleCallbackSuccess 处理回调成功
|
||||
func handleCallbackSuccess(ctx context.Context, req *dto.CallbackReq, composeTask *entity.ComposeTask, messages map[string]any) error {
|
||||
// 1) 获取模型配置
|
||||
model, err := gateway.GetModelConfig(ctx, &gateway.AsynchModel{
|
||||
SQLBaseDO: beans.SQLBaseDO{Creator: composeTask.Creator},
|
||||
ModelName: composeTask.ModelName,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("查询模型失败: %w", err)
|
||||
}
|
||||
|
||||
// 2) 获取协议配置
|
||||
protocol, _ := dao.ProviderProtocol.Get(ctx, &entity.ProviderProtocol{
|
||||
ProviderName: model.OperatorName,
|
||||
Status: 1,
|
||||
})
|
||||
|
||||
// 3) 获取历史消息 + 保存当前轮
|
||||
payload := composeTask.RequestPayload
|
||||
sessionId := gconv.String(payload["sessionId"])
|
||||
nodeId := gconv.String(payload["nodeId"])
|
||||
var history []dto.FlatMessage
|
||||
var epicycleId int64
|
||||
|
||||
if sessionId != "" && nodeId != "" && model.ModelType == public.ModelTypeInference {
|
||||
// 3.1 获取历史
|
||||
h, _ := session.GetHistoryMessages(ctx, &dto.GetHistoryMessagesReq{
|
||||
SessionId: sessionId,
|
||||
NodeId: nodeId,
|
||||
})
|
||||
if h != nil {
|
||||
history = h.Messages
|
||||
}
|
||||
|
||||
// 3.2 保存当前轮(先存,下次查询就能拿到)
|
||||
if userMsg := util.ExtractUserText(messages); userMsg != nil {
|
||||
epicycleId, _ = dao.ComposeSession.Insert(ctx, &entity.ComposeSession{
|
||||
NodeId: nodeId,
|
||||
SessionId: sessionId,
|
||||
RequestContent: userMsg,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 4) 合并附加结构
|
||||
messages = util.MergeConsult(composeTask.RequestPayload, messages, model.ExtendMapping)
|
||||
// 5) 注入历史
|
||||
if len(history) > 0 {
|
||||
messages = InjectHistory(messages, history, protocol)
|
||||
}
|
||||
|
||||
// 6) 更新数据库
|
||||
_, err = dao.ComposeTask.Update(ctx, &entity.ComposeTask{
|
||||
TaskId: req.TaskId,
|
||||
Status: public.ComposeStatusSuccess,
|
||||
GatewayState: req.State,
|
||||
OssFile: req.OssFile,
|
||||
FileType: req.FileType,
|
||||
ResultJson: messages,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 8) 回调业务方
|
||||
if composeTask.CallbackUrl != "" {
|
||||
composeTask.Status = public.ComposeStatusSuccess
|
||||
composeTask.ResultJson = messages
|
||||
_ = gateway.SendCallback(ctx, composeTask, epicycleId)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InjectHistory 插入历史会话
|
||||
func InjectHistory(roundsData map[string]any, history []dto.FlatMessage, protocol *entity.ProviderProtocol) map[string]any {
|
||||
if protocol == nil || len(history) == 0 {
|
||||
return roundsData
|
||||
}
|
||||
// 1) 提取第一轮的 messages
|
||||
rounds := roundsData["rounds"].([]any)
|
||||
firstRound := rounds[0].(map[string]any)
|
||||
original := firstRound["messages"].([]any)
|
||||
|
||||
// 2) 按 merge_order 拼接
|
||||
result := make([]any, 0, len(original)+len(history))
|
||||
|
||||
for _, part := range protocol.MergeOrder {
|
||||
switch part {
|
||||
case "system":
|
||||
for _, m := range original {
|
||||
msg := m.(map[string]any)
|
||||
if gconv.String(msg["role"]) == "system" {
|
||||
result = append(result, msg)
|
||||
}
|
||||
}
|
||||
case "history":
|
||||
if gconv.Bool(protocol.Capabilities["support_history"]) {
|
||||
for _, msg := range history {
|
||||
result = append(result, map[string]any{
|
||||
"role": msg.Role,
|
||||
"content": msg.Content, // 纯字符串,不转换
|
||||
})
|
||||
}
|
||||
}
|
||||
case "user":
|
||||
for _, m := range original {
|
||||
msg := m.(map[string]any)
|
||||
if gconv.String(msg["role"]) == "user" {
|
||||
result = append(result, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3) 角色映射
|
||||
if len(protocol.RoleMapping) > 0 {
|
||||
for _, m := range result {
|
||||
msg := m.(map[string]any)
|
||||
role := gconv.String(msg["role"])
|
||||
if mapped, ok := protocol.RoleMapping[role]; ok {
|
||||
msg["role"] = mapped
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4) 直接修改原对象
|
||||
firstRound["messages"] = result
|
||||
return roundsData
|
||||
}
|
||||
|
||||
// GetComposeTask 查询任务结果
|
||||
func GetComposeTask(ctx context.Context, taskID string) (*dto.GetComposeTaskRes, error) {
|
||||
record, err := dao.ComposeTask.Get(ctx, &entity.ComposeTask{
|
||||
TaskId: taskID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询任务失败: %w", err)
|
||||
}
|
||||
return &dto.GetComposeTaskRes{
|
||||
TaskId: record.TaskId,
|
||||
Status: record.Status,
|
||||
ErrorMessage: record.ErrorMessage,
|
||||
Messages: record.ResultJson,
|
||||
}, nil
|
||||
}
|
||||
295
service/prompt/prompt_files_handle_service.go
Normal file
295
service/prompt/prompt_files_handle_service.go
Normal file
@@ -0,0 +1,295 @@
|
||||
package prompt
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"prompts-core/model/dto"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
"prompts-core/common/util"
|
||||
"prompts-core/service/gateway"
|
||||
)
|
||||
|
||||
const (
|
||||
bytesPerKB = 1024
|
||||
bytesPerMB = 1024 * 1024
|
||||
)
|
||||
|
||||
// ExtractFileTexts 从 ConsultItem 列表中提取文件内容,返回拼接文本
|
||||
func ExtractFileTexts(ctx context.Context, consult []dto.ConsultItem) string {
|
||||
urls := make([]string, 0, len(consult))
|
||||
for _, item := range consult {
|
||||
if item.Url != "" {
|
||||
urls = append(urls, item.Url)
|
||||
}
|
||||
}
|
||||
return FetchFileTextsAsString(ctx, urls)
|
||||
}
|
||||
|
||||
// FetchFileTextsAsString 从 URL 列表获取文件内容,拼接为字符串
|
||||
func FetchFileTextsAsString(ctx context.Context, urls []string) string {
|
||||
if len(urls) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
client := createHTTPClient(ctx, "userFiles.httpTimeoutSec", 8)
|
||||
var builder strings.Builder
|
||||
|
||||
for _, rawURL := range urls {
|
||||
url := util.SanitizeURL(rawURL)
|
||||
if url == "" || util.IsBannedExtension(url) {
|
||||
continue
|
||||
}
|
||||
|
||||
if util.IsZipExtension(url) {
|
||||
for _, text := range fetchZipFileTexts(ctx, client, url) {
|
||||
builder.WriteString(text)
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if text := fetchAndCleanFileContent(ctx, client, url); text != "" {
|
||||
builder.WriteString(fmt.Sprintf("【文件:%s】\n%s\n", url, text))
|
||||
}
|
||||
}
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
// fetchAndCleanFileContent 获取并清理文件内容
|
||||
func fetchAndCleanFileContent(ctx context.Context, client *http.Client, url string) string {
|
||||
text, err := fetchFileContent(ctx, client, url)
|
||||
if err != nil || text == "" {
|
||||
return ""
|
||||
}
|
||||
return util.CleanSymbols(text)
|
||||
}
|
||||
|
||||
// fetchZipFileTexts 下载并解压 zip 文件,提取可读文本内容
|
||||
func fetchZipFileTexts(ctx context.Context, client *http.Client, url string) map[string]string {
|
||||
result := make(map[string]string)
|
||||
|
||||
maxSize := int64(g.Cfg().MustGet(ctx, "userFiles.zipMaxSizeMB", 10).Int()) * bytesPerMB
|
||||
zipBytes, err := downloadFile(client, url, maxSize)
|
||||
if err != nil {
|
||||
return result
|
||||
}
|
||||
|
||||
reader, err := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes)))
|
||||
if err != nil {
|
||||
return result
|
||||
}
|
||||
|
||||
entryMaxSize := int64(g.Cfg().MustGet(ctx, "userFiles.zipEntryMaxSizeKB", 500).Int()) * bytesPerKB
|
||||
|
||||
for _, file := range reader.File {
|
||||
if shouldSkipZipEntry(file.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
if text := extractZipEntryContent(file, entryMaxSize); text != "" {
|
||||
result[url+"::"+file.Name] = text
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// shouldSkipZipEntry 判断是否应该跳过 zip 条目
|
||||
func shouldSkipZipEntry(fileName string) bool {
|
||||
return util.IsBannedExtension(fileName) || util.IsZipExtension(fileName)
|
||||
}
|
||||
|
||||
// extractZipEntryContent 提取 zip 条目内容
|
||||
func extractZipEntryContent(file *zip.File, maxSize int64) string {
|
||||
rc, err := file.Open()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
content, err := io.ReadAll(io.LimitReader(rc, maxSize))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if !util.IsReadableContentType(http.DetectContentType(content)) {
|
||||
return ""
|
||||
}
|
||||
|
||||
text := util.CleanSymbols(string(content))
|
||||
if text == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
// downloadFile 下载文件,限制最大大小
|
||||
func downloadFile(client *http.Client, url string, maxSize int64) ([]byte, error) {
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建请求失败: %w", err)
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("执行请求失败: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return nil, fmt.Errorf("HTTP %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(io.LimitReader(resp.Body, maxSize))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取响应失败: %w", err)
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// fetchFileContent 获取单个文本文件内容
|
||||
func fetchFileContent(ctx context.Context, client *http.Client, url string) (string, error) {
|
||||
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("创建请求失败: %w", err)
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("执行请求失败: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
return "", fmt.Errorf("HTTP %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
contentType := resp.Header.Get("Content-Type")
|
||||
if !util.IsReadableContentType(contentType) {
|
||||
return "", fmt.Errorf("不可读的内容类型: %s", contentType)
|
||||
}
|
||||
|
||||
maxSize := int64(g.Cfg().MustGet(ctx, "userFiles.textFileMaxSizeKB", 500).Int()) * bytesPerKB
|
||||
body, err := io.ReadAll(io.LimitReader(resp.Body, maxSize))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("读取响应失败: %w", err)
|
||||
}
|
||||
|
||||
return strings.TrimSpace(string(body)), nil
|
||||
}
|
||||
|
||||
func SkillMdContent(ctx context.Context, skillName string) string {
|
||||
if skillName == "" {
|
||||
return ""
|
||||
}
|
||||
skillResp, err := gateway.GetSkillUser(ctx, skillName)
|
||||
if err != nil {
|
||||
g.Log().Warningf(ctx, "[SkillMd] GetSkillUser 失败: %v", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
fullUrl := skillResp.ImgAddressPrefix + skillResp.FileUrl
|
||||
|
||||
client := createHTTPClient(ctx, "skillFiles.httpTimeoutSec", 30)
|
||||
maxSize := int64(g.Cfg().MustGet(ctx, "skillFiles.zipMaxSizeMB", 10).Int()) * bytesPerMB
|
||||
|
||||
zipBytes, err := downloadFile(client, fullUrl, maxSize)
|
||||
if err != nil {
|
||||
g.Log().Warningf(ctx, "[SkillMd] 下载失败 url=%s err=%v", fullUrl, err)
|
||||
return ""
|
||||
}
|
||||
|
||||
mdContents, err := extractMdFiles(ctx, zipBytes)
|
||||
if err != nil || len(mdContents) == 0 {
|
||||
g.Log().Warningf(ctx, "[SkillMd] 提取md失败 count=%d err=%v", len(mdContents), err)
|
||||
return ""
|
||||
}
|
||||
|
||||
return buildSkillMarkdown(skillResp, mdContents)
|
||||
}
|
||||
|
||||
// buildSkillMarkdown 构建技能 Markdown 内容
|
||||
func buildSkillMarkdown(skillResp *gateway.SkillUserVO, mdContents map[string]string) string {
|
||||
var builder strings.Builder
|
||||
|
||||
builder.WriteString(fmt.Sprintf("# Skill: %s\n\n", skillResp.Name))
|
||||
if skillResp.Description != "" {
|
||||
builder.WriteString(fmt.Sprintf("> %s\n\n", skillResp.Description))
|
||||
}
|
||||
|
||||
for fileName, content := range mdContents {
|
||||
builder.WriteString(fmt.Sprintf("## %s\n\n", fileName))
|
||||
builder.WriteString(content)
|
||||
builder.WriteString("\n\n---\n\n")
|
||||
}
|
||||
|
||||
return strings.TrimSpace(builder.String())
|
||||
}
|
||||
|
||||
// extractMdFiles 解压 zip 并提取所有 .md 文件内容
|
||||
func extractMdFiles(ctx context.Context, zipBytes []byte) (map[string]string, error) {
|
||||
result := make(map[string]string)
|
||||
|
||||
reader, err := zip.NewReader(bytes.NewReader(zipBytes), int64(len(zipBytes)))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("创建 zip 阅读器失败: %w", err)
|
||||
}
|
||||
|
||||
entryMaxSize := int64(g.Cfg().MustGet(ctx, "skillFiles.mdMaxSizeKB", 500).Int()) * bytesPerKB
|
||||
|
||||
for _, file := range reader.File {
|
||||
if file.FileInfo().IsDir() || !isMarkdownFile(file.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
if content := readMarkdownFileContent(file, entryMaxSize); content != "" {
|
||||
result[file.Name] = content
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// isMarkdownFile 判断是否为 Markdown 文件
|
||||
func isMarkdownFile(fileName string) bool {
|
||||
return strings.HasSuffix(strings.ToLower(fileName), ".md")
|
||||
}
|
||||
|
||||
// readMarkdownFileContent 读取 Markdown 文件内容
|
||||
func readMarkdownFileContent(file *zip.File, maxSize int64) string {
|
||||
rc, err := file.Open()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
content, err := io.ReadAll(io.LimitReader(rc, maxSize))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if len(content) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
return strings.TrimSpace(string(content))
|
||||
}
|
||||
|
||||
// createHTTPClient 创建 HTTP 客户端
|
||||
func createHTTPClient(ctx context.Context, configKey string, defaultSeconds int) *http.Client {
|
||||
timeout := time.Duration(g.Cfg().MustGet(ctx, configKey, defaultSeconds).Int()) * time.Second
|
||||
return &http.Client{
|
||||
Timeout: timeout,
|
||||
}
|
||||
}
|
||||
285
service/prompt/prompt_ir_service.go
Normal file
285
service/prompt/prompt_ir_service.go
Normal file
@@ -0,0 +1,285 @@
|
||||
package prompt
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"prompts-core/service/gateway"
|
||||
"strings"
|
||||
|
||||
"prompts-core/dao"
|
||||
"prompts-core/model/entity"
|
||||
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// IR 统一 Prompt 中间表示
|
||||
type IR struct {
|
||||
System []Segment `json:"system"`
|
||||
History []Segment `json:"history"`
|
||||
User []Segment `json:"user"`
|
||||
}
|
||||
|
||||
// Segment 消息片段
|
||||
type Segment struct {
|
||||
Type string `json:"type"`
|
||||
Content string `json:"content"`
|
||||
Role string `json:"role,omitempty"`
|
||||
}
|
||||
|
||||
// ProviderProtocol 协议编译配置(从 DB JSONB 字段解析)
|
||||
type ProviderProtocol struct {
|
||||
TargetField string `json:"target_field"`
|
||||
MergeOrder []string `json:"merge_order"`
|
||||
RoleMapping map[string]string `json:"role_mapping"`
|
||||
ContentMapping ContentMapping `json:"content_mapping"`
|
||||
RequestTemplate map[string]any `json:"request_template"`
|
||||
SystemPromptTemplate string `json:"system_prompt_template"`
|
||||
Capabilities map[string]any `json:"capabilities"`
|
||||
}
|
||||
|
||||
// ContentMapping 内容字段映射
|
||||
type ContentMapping struct {
|
||||
Type string `json:"type"`
|
||||
Field string `json:"field"`
|
||||
}
|
||||
|
||||
// NewPromptIR 创建空 PromptIR
|
||||
func NewPromptIR() *IR {
|
||||
return &IR{
|
||||
System: make([]Segment, 0),
|
||||
History: make([]Segment, 0),
|
||||
User: make([]Segment, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// String 返回 PromptIR 的完整内容字符串(用于 token 计算)
|
||||
func (ir *IR) String() string {
|
||||
var builder strings.Builder
|
||||
|
||||
for _, seg := range ir.System {
|
||||
builder.WriteString("System: ")
|
||||
builder.WriteString(seg.Content)
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
|
||||
for _, seg := range ir.History {
|
||||
builder.WriteString(seg.Role)
|
||||
builder.WriteString(": ")
|
||||
builder.WriteString(seg.Content)
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
|
||||
for _, seg := range ir.User {
|
||||
builder.WriteString("User: ")
|
||||
builder.WriteString(seg.Content)
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
// GetTotalContent 获取所有内容的拼接字符串(更精确的 token 计算)
|
||||
func (ir *IR) GetTotalContent() string {
|
||||
var builder strings.Builder
|
||||
|
||||
for _, seg := range ir.System {
|
||||
builder.WriteString(seg.Content)
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
|
||||
for _, seg := range ir.History {
|
||||
builder.WriteString(seg.Content)
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
|
||||
for _, seg := range ir.User {
|
||||
builder.WriteString(seg.Content)
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
// AddSystem 添加系统提示
|
||||
func (ir *IR) AddSystem(content string) *IR {
|
||||
if content != "" {
|
||||
ir.System = append(ir.System, Segment{Type: "text", Content: content})
|
||||
}
|
||||
return ir
|
||||
}
|
||||
|
||||
// AddUser 添加用户消息
|
||||
func (ir *IR) AddUser(content string) *IR {
|
||||
if content != "" {
|
||||
ir.User = append(ir.User, Segment{Type: "text", Content: content})
|
||||
}
|
||||
return ir
|
||||
}
|
||||
|
||||
// AddHistory 添加历史消息
|
||||
func (ir *IR) AddHistory(role, content string) *IR {
|
||||
if content != "" {
|
||||
ir.History = append(ir.History, Segment{Type: "text", Content: content, Role: role})
|
||||
}
|
||||
return ir
|
||||
}
|
||||
|
||||
// ToMessages 转换为 OpenAI 兼容的 messages 格式(MVP 默认)
|
||||
func (ir *IR) ToMessages() []map[string]any {
|
||||
var messages []map[string]any
|
||||
|
||||
for _, seg := range ir.System {
|
||||
messages = append(messages, map[string]any{
|
||||
"role": "system",
|
||||
"content": seg.Content,
|
||||
})
|
||||
}
|
||||
|
||||
for _, seg := range ir.History {
|
||||
messages = append(messages, map[string]any{
|
||||
"role": seg.Role,
|
||||
"content": seg.Content,
|
||||
})
|
||||
}
|
||||
|
||||
for _, seg := range ir.User {
|
||||
messages = append(messages, map[string]any{
|
||||
"role": "user",
|
||||
"content": seg.Content,
|
||||
})
|
||||
}
|
||||
|
||||
return messages
|
||||
}
|
||||
|
||||
// GetProtocolByProvider 根据 provider_name 获取协议配置
|
||||
func GetProtocolByProvider(ctx context.Context, providerName string) (*ProviderProtocol, error) {
|
||||
entity, err := dao.ProviderProtocol.Get(ctx, &entity.ProviderProtocol{
|
||||
ProviderName: providerName,
|
||||
Status: 1,
|
||||
})
|
||||
if err != nil || entity == nil {
|
||||
return nil, err
|
||||
}
|
||||
return parseProtocol(entity), nil
|
||||
}
|
||||
|
||||
// parseProtocol 将 DB entity 转为编译用协议配置
|
||||
func parseProtocol(e *entity.ProviderProtocol) *ProviderProtocol {
|
||||
return &ProviderProtocol{
|
||||
TargetField: e.TargetField,
|
||||
SystemPromptTemplate: e.SystemPromptTemplate,
|
||||
MergeOrder: e.MergeOrder,
|
||||
RoleMapping: gconv.MapStrStr(e.RoleMapping),
|
||||
ContentMapping: ContentMapping{
|
||||
Type: gconv.String(e.ContentMapping["type"]),
|
||||
Field: gconv.String(e.ContentMapping["field"]),
|
||||
},
|
||||
RequestTemplate: e.RequestTemplate,
|
||||
Capabilities: e.Capabilities,
|
||||
}
|
||||
}
|
||||
|
||||
// Compile 将 PromptIR 按协议配置编译为 Provider Request
|
||||
func Compile(ir *IR, p *ProviderProtocol, chatModel *gateway.AsynchModel) (map[string]any, error) {
|
||||
if ir == nil || p == nil {
|
||||
return nil, fmt.Errorf("ir and protocol are required")
|
||||
}
|
||||
messages := mergeByOrder(ir, p.MergeOrder)
|
||||
messages = mapRoles(messages, p.RoleMapping)
|
||||
messages = mapContent(messages, p.ContentMapping)
|
||||
|
||||
return buildRequest(messages, p, chatModel), nil
|
||||
}
|
||||
|
||||
// mergeByOrder 按协议配置顺序拼接消息
|
||||
func mergeByOrder(ir *IR, order []string) []map[string]any {
|
||||
roleMap := map[string][]Segment{
|
||||
"system": ir.System,
|
||||
"history": ir.History,
|
||||
"user": ir.User,
|
||||
}
|
||||
|
||||
var messages []map[string]any
|
||||
for _, part := range order {
|
||||
for _, seg := range roleMap[part] {
|
||||
msg := map[string]any{"content": seg.Content}
|
||||
if part == "history" {
|
||||
msg["role"] = seg.Role
|
||||
} else {
|
||||
msg["role"] = part
|
||||
}
|
||||
messages = append(messages, msg)
|
||||
}
|
||||
}
|
||||
return messages
|
||||
}
|
||||
|
||||
// mapRoles 角色映射
|
||||
func mapRoles(messages []map[string]any, mapping map[string]string) []map[string]any {
|
||||
if len(mapping) == 0 {
|
||||
return messages
|
||||
}
|
||||
|
||||
for i, msg := range messages {
|
||||
role, ok := msg["role"].(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if mapped, exists := mapping[role]; exists {
|
||||
messages[i]["role"] = mapped
|
||||
}
|
||||
}
|
||||
|
||||
return messages
|
||||
}
|
||||
|
||||
func mapContent(messages []map[string]any, cm ContentMapping) []map[string]any {
|
||||
if cm.Field == "" || cm.Field == "content" {
|
||||
return messages
|
||||
}
|
||||
|
||||
for i, msg := range messages {
|
||||
if content, ok := msg["content"]; ok {
|
||||
delete(msg, "content")
|
||||
switch cm.Type {
|
||||
case "parts":
|
||||
messages[i]["parts"] = []map[string]any{{cm.Field: content}}
|
||||
default:
|
||||
messages[i][cm.Field] = content
|
||||
}
|
||||
}
|
||||
}
|
||||
return messages
|
||||
}
|
||||
|
||||
// buildRequest 按 target_field 和 request_template 构建请求体
|
||||
func buildRequest(messages []map[string]any, p *ProviderProtocol, chatModel *gateway.AsynchModel) map[string]any {
|
||||
if len(p.RequestTemplate) > 0 {
|
||||
return renderTemplate(p, messages, chatModel)
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
p.TargetField: messages,
|
||||
}
|
||||
}
|
||||
|
||||
// renderTemplate 模板渲染
|
||||
func renderTemplate(p *ProviderProtocol, messages []map[string]any, chatModel *gateway.AsynchModel) map[string]any {
|
||||
result := make(map[string]any, len(p.RequestTemplate)+1)
|
||||
for k, v := range p.RequestTemplate {
|
||||
result[k] = v
|
||||
}
|
||||
|
||||
if chatModel != nil {
|
||||
result["model"] = chatModel.ModelName
|
||||
}
|
||||
result["messages"] = messages
|
||||
|
||||
if maxTokens := gconv.Int(p.Capabilities["max_tokens"]); maxTokens > 0 {
|
||||
result["max_tokens"] = maxTokens
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
135
service/prompt/prompt_user_form_batches.go
Normal file
135
service/prompt/prompt_user_form_batches.go
Normal file
@@ -0,0 +1,135 @@
|
||||
package prompt
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"prompts-core/service/gateway"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
"prompts-core/common/util"
|
||||
"prompts-core/model/dto"
|
||||
)
|
||||
|
||||
// ProcessUserFormBatches 处理 UserForm 分批(按 token 大小拼接内容)
|
||||
func ProcessUserFormBatches(ctx context.Context, req *dto.ComposeMessagesReq, model *gateway.AsynchModel) (*dto.ComposeMessagesReq, int, error) {
|
||||
if model.TokenConfig == nil || len(req.UserForm) == 0 {
|
||||
return req, 1, nil
|
||||
}
|
||||
|
||||
availableWindow := util.GetAvailableWindow(model.TokenConfig)
|
||||
batches := splitUserFormByTokenSize(req.UserForm, availableWindow, model.TokenConfig)
|
||||
|
||||
if len(batches) <= 1 {
|
||||
return req, 1, nil
|
||||
}
|
||||
|
||||
newUserForm := buildBatchedUserForm(batches)
|
||||
|
||||
newReq := *req
|
||||
newReq.UserForm = newUserForm
|
||||
|
||||
g.Log().Infof(ctx, "[ProcessUserFormBatches] UserForm分批完成: 原始%d条 -> %d批 (按token大小拼接)",
|
||||
len(req.UserForm), len(batches))
|
||||
|
||||
return &newReq, len(batches), nil
|
||||
}
|
||||
|
||||
// buildBatchedUserForm 构建分批后的用户表单
|
||||
func buildBatchedUserForm(batches [][]map[string]any) []map[string]any {
|
||||
newUserForm := make([]map[string]any, 0, len(batches))
|
||||
|
||||
for i, batch := range batches {
|
||||
combinedText := combineBatchText(batch)
|
||||
newUserForm = append(newUserForm, map[string]any{
|
||||
"batch_index": i + 1,
|
||||
"total_batches": len(batches),
|
||||
"text": combinedText,
|
||||
"item_count": len(batch),
|
||||
})
|
||||
}
|
||||
|
||||
return newUserForm
|
||||
}
|
||||
|
||||
// combineBatchText 合并批次中的所有文本(合并所有字段的值)
|
||||
func combineBatchText(batch []map[string]any) string {
|
||||
var builder strings.Builder
|
||||
|
||||
for j, item := range batch {
|
||||
itemText := getItemText(item)
|
||||
if itemText == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if j > 0 {
|
||||
builder.WriteString("\n\n")
|
||||
}
|
||||
builder.WriteString(itemText)
|
||||
}
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
// splitUserFormByTokenSize 按 token 大小将 UserForm 内容拼接后分批
|
||||
func splitUserFormByTokenSize(userForm []map[string]any, maxTokens int, tokenConfig any) [][]map[string]any {
|
||||
if len(userForm) == 0 {
|
||||
return [][]map[string]any{}
|
||||
}
|
||||
|
||||
batches := make([][]map[string]any, 0)
|
||||
currentBatch := make([]map[string]any, 0)
|
||||
currentTokens := 0
|
||||
|
||||
for i, item := range userForm {
|
||||
itemText := getItemText(item)
|
||||
itemTokens := util.CalculateTokens(itemText, tokenConfig)
|
||||
|
||||
// 单个元素超过窗口,单独成一批
|
||||
if itemTokens > maxTokens {
|
||||
if len(currentBatch) > 0 {
|
||||
batches = append(batches, currentBatch)
|
||||
currentBatch = make([]map[string]any, 0)
|
||||
currentTokens = 0
|
||||
}
|
||||
batches = append(batches, []map[string]any{item})
|
||||
continue
|
||||
}
|
||||
|
||||
// 判断是否需要新开一批
|
||||
if currentTokens+itemTokens > maxTokens && len(currentBatch) > 0 {
|
||||
batches = append(batches, currentBatch)
|
||||
currentBatch = make([]map[string]any, 0)
|
||||
currentTokens = 0
|
||||
}
|
||||
|
||||
currentBatch = append(currentBatch, item)
|
||||
currentTokens += itemTokens
|
||||
|
||||
// 最后一批
|
||||
if i == len(userForm)-1 && len(currentBatch) > 0 {
|
||||
batches = append(batches, currentBatch)
|
||||
}
|
||||
}
|
||||
|
||||
return batches
|
||||
}
|
||||
|
||||
// getItemText 获取 item 中的所有文本内容(合并所有字段的值)
|
||||
func getItemText(item map[string]any) string {
|
||||
if len(item) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var parts []string
|
||||
for key, value := range item {
|
||||
// 跳过分批时添加的元数据字段
|
||||
if key == "batch_index" || key == "total_batches" || key == "item_count" {
|
||||
continue
|
||||
}
|
||||
parts = append(parts, fmt.Sprintf("%v", value))
|
||||
}
|
||||
|
||||
return strings.Join(parts, "\n")
|
||||
}
|
||||
151
service/session/prompt_session_redis_service.go
Normal file
151
service/session/prompt_session_redis_service.go
Normal file
@@ -0,0 +1,151 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"prompts-core/common/util"
|
||||
"prompts-core/model/dto"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
const (
|
||||
// RedisKeySessionHistory 会话历史缓存 key: session:history:{tenantId}:{sessionId}:{nodeId}
|
||||
RedisKeySessionHistory = "session:history:%d:%s:%s"
|
||||
)
|
||||
|
||||
// formatRedisKey 格式化 Redis key
|
||||
func formatRedisKey(tenantID uint64, sessionID, nodeID string) string {
|
||||
return fmt.Sprintf(RedisKeySessionHistory, tenantID, sessionID, nodeID)
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 写操作
|
||||
// ============================================
|
||||
|
||||
// SaveToRedis 保存一轮对话到 Redis ZSET
|
||||
func SaveToRedis(ctx context.Context, tenantID uint64, sessionID, nodeID string, round *dto.HistoryRound) error {
|
||||
key := formatRedisKey(tenantID, sessionID, nodeID)
|
||||
maxRounds := util.GetMaxRounds(ctx)
|
||||
expireSeconds := int64(util.GetExpireMinutes(ctx) * 60)
|
||||
|
||||
b, err := json.Marshal(round)
|
||||
if err != nil {
|
||||
return fmt.Errorf("序列化会话数据失败: %w", err)
|
||||
}
|
||||
|
||||
score := float64(time.Now().UnixMilli())
|
||||
|
||||
if _, err = g.Redis().Do(ctx, "ZADD", key, score, string(b)); err != nil {
|
||||
return fmt.Errorf("ZADD失败: %w", err)
|
||||
}
|
||||
if _, err = g.Redis().Do(ctx, "ZREMRANGEBYRANK", key, 0, -(maxRounds + 1)); err != nil {
|
||||
return fmt.Errorf("裁剪失败: %w", err)
|
||||
}
|
||||
if _, err = g.Redis().Do(ctx, "EXPIRE", key, expireSeconds); err != nil {
|
||||
return fmt.Errorf("设置过期失败: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteSessionHistory 删除整个 session 下所有 node 的缓存
|
||||
func DeleteSessionHistory(ctx context.Context, tenantID uint64, sessionID string) error {
|
||||
pattern := fmt.Sprintf(RedisKeySessionHistory, tenantID, sessionID, "*")
|
||||
keys, err := g.Redis().Do(ctx, "KEYS", pattern)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, key := range keys.Strings() {
|
||||
_, _ = g.Redis().Do(ctx, "DEL", key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteRedisMessages 批量删除指定 node 下的消息
|
||||
func DeleteRedisMessages(ctx context.Context, tenantID uint64, sessionID, nodeID string, msgIDs []int64) error {
|
||||
key := formatRedisKey(tenantID, sessionID, nodeID)
|
||||
for _, msgID := range msgIDs {
|
||||
cursor := "0"
|
||||
for {
|
||||
result, err := g.Redis().Do(ctx, "ZSCAN", key, cursor, "MATCH", fmt.Sprintf("*\"id\":%d*", msgID), "COUNT", 10)
|
||||
if err != nil {
|
||||
g.Log().Warningf(ctx, "[会话Redis] ZSCAN失败 msgID=%d err=%v", msgID, err)
|
||||
break
|
||||
}
|
||||
parts := result.Strings()
|
||||
if len(parts) < 2 {
|
||||
break
|
||||
}
|
||||
cursor = parts[0]
|
||||
for _, member := range parts[1:] {
|
||||
_, _ = g.Redis().Do(ctx, "ZREM", key, member)
|
||||
}
|
||||
if cursor == "0" {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 读操作
|
||||
// ============================================
|
||||
|
||||
// GetFromRedis 从 Redis ZSET 获取会话历史
|
||||
func GetFromRedis(ctx context.Context, tenantID uint64, sessionID, nodeID string) ([]dto.HistoryRound, error) {
|
||||
key := formatRedisKey(tenantID, sessionID, nodeID)
|
||||
maxRounds := util.GetMaxRounds(ctx)
|
||||
|
||||
result, err := g.Redis().Do(ctx, "ZREVRANGE", key, 0, maxRounds-1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ZREVRANGE失败: %w", err)
|
||||
}
|
||||
|
||||
if result == nil || result.IsNil() {
|
||||
return []dto.HistoryRound{}, nil
|
||||
}
|
||||
|
||||
return parseRounds(result.Strings()), nil
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 解析
|
||||
// ============================================
|
||||
|
||||
func parseRounds(members []string) []dto.HistoryRound {
|
||||
rounds := make([]dto.HistoryRound, 0, len(members))
|
||||
for _, member := range members {
|
||||
var round dto.HistoryRound
|
||||
if err := json.Unmarshal([]byte(member), &round); err != nil {
|
||||
continue
|
||||
}
|
||||
if round.User != nil || round.Assistant != nil {
|
||||
rounds = append(rounds, round)
|
||||
}
|
||||
}
|
||||
return rounds
|
||||
}
|
||||
|
||||
func flattenRounds(rounds []dto.HistoryRound) []dto.FlatMessage {
|
||||
var messages []dto.FlatMessage
|
||||
for i := len(rounds) - 1; i >= 0; i-- {
|
||||
if rounds[i].User != nil && gconv.String(rounds[i].User["content"]) != "" {
|
||||
messages = append(messages, dto.FlatMessage{
|
||||
Role: gconv.String(rounds[i].User["role"]),
|
||||
Content: gconv.String(rounds[i].User["content"]),
|
||||
})
|
||||
}
|
||||
if rounds[i].Assistant != nil && gconv.String(rounds[i].Assistant["content"]) != "" {
|
||||
messages = append(messages, dto.FlatMessage{
|
||||
Role: gconv.String(rounds[i].Assistant["role"]),
|
||||
Content: gconv.String(rounds[i].Assistant["content"]),
|
||||
})
|
||||
}
|
||||
}
|
||||
return messages
|
||||
}
|
||||
191
service/session/prompt_session_service.go
Normal file
191
service/session/prompt_session_service.go
Normal file
@@ -0,0 +1,191 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
"gitea.redpowerfuture.com/red-future/common/utils"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
|
||||
"prompts-core/common/util"
|
||||
"prompts-core/dao"
|
||||
"prompts-core/model/dto"
|
||||
"prompts-core/model/entity"
|
||||
)
|
||||
|
||||
// ============================================
|
||||
// 回调存储
|
||||
// ============================================
|
||||
|
||||
// Callback 会话回调
|
||||
func Callback(ctx context.Context, req *dto.SessionCallbackReq) (*dto.SessionCallbackRes, error) {
|
||||
req.Messages["role"] = "assistant"
|
||||
// 1) 更新 DB
|
||||
_, err := dao.ComposeSession.Update(ctx, &entity.ComposeSession{
|
||||
SQLBaseDO: beans.SQLBaseDO{Id: req.EpicycleId},
|
||||
ResponseContent: req.Messages,
|
||||
})
|
||||
if err != nil {
|
||||
g.Log().Errorf(ctx, "[会话回调] 更新数据库失败 epicycleId=%d err=%v", req.EpicycleId, err)
|
||||
return nil, fmt.Errorf("更新数据库失败: %w", err)
|
||||
}
|
||||
|
||||
// 2) 查询完整记录
|
||||
session, err := dao.ComposeSession.Get(ctx, &entity.ComposeSession{
|
||||
SQLBaseDO: beans.SQLBaseDO{Id: req.EpicycleId},
|
||||
})
|
||||
if err != nil || session == nil {
|
||||
return nil, fmt.Errorf("会话不存在: epicycleId=%d", req.EpicycleId)
|
||||
}
|
||||
|
||||
// 3) entity → HistoryRound → 写入 Redis
|
||||
round := entityToHistoryRound(session)
|
||||
round.Assistant = req.Messages
|
||||
if err = SaveToRedis(ctx, session.TenantId, session.SessionId, session.NodeId, round); err != nil {
|
||||
return nil, fmt.Errorf("redis存储失败: %w", err)
|
||||
}
|
||||
|
||||
g.Log().Infof(ctx, "[会话回调] 存储成功 sessionId=%s id=%d", session.SessionId, session.Id)
|
||||
return &dto.SessionCallbackRes{Status: true, SessionId: session.SessionId}, nil
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 场景1:前端历史列表(按 creator)
|
||||
// ============================================
|
||||
|
||||
// GetHistoryList 获取历史列表
|
||||
func GetHistoryList(ctx context.Context, req *dto.GetHistoryListReq) (*dto.GetHistoryListRes, error) {
|
||||
user, err := utils.GetUserInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sessions, total, err := dao.ComposeSession.List(ctx, &entity.ComposeSession{
|
||||
SQLBaseDO: beans.SQLBaseDO{Creator: user.UserName},
|
||||
}, req.Page, req.Size)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("DB获取历史列表失败: %w", err)
|
||||
}
|
||||
rounds := sessionsToHistoryRounds(sessions)
|
||||
return &dto.GetHistoryListRes{List: rounds, Total: total}, nil
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 场景2:提示词拼接(按 sessionId + nodeId)
|
||||
// ============================================
|
||||
|
||||
// GetHistoryMessages 获取历史消息(Redis → DB → 异步回种)
|
||||
func GetHistoryMessages(ctx context.Context, req *dto.GetHistoryMessagesReq) (*dto.GetHistoryMessagesRes, error) {
|
||||
user, err := utils.GetUserInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 1) Redis
|
||||
if rounds, err := GetFromRedis(ctx, user.TenantId, req.SessionId, req.NodeId); err == nil && len(rounds) > 0 {
|
||||
g.Log().Debugf(ctx, "[历史消息] Redis命中 sessionId=%s count=%d", req.SessionId, len(rounds))
|
||||
return &dto.GetHistoryMessagesRes{Messages: flattenRounds(rounds)}, nil
|
||||
}
|
||||
|
||||
// 2) DB
|
||||
maxRounds := util.GetMaxRounds(ctx)
|
||||
sessions, _, err := dao.ComposeSession.List(ctx, &entity.ComposeSession{
|
||||
SQLBaseDO: beans.SQLBaseDO{Creator: user.UserName},
|
||||
SessionId: req.SessionId,
|
||||
NodeId: req.NodeId,
|
||||
}, 1, maxRounds)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("DB获取历史失败: %w", err)
|
||||
}
|
||||
if len(sessions) == 0 {
|
||||
return &dto.GetHistoryMessagesRes{Messages: []dto.FlatMessage{}}, nil
|
||||
}
|
||||
|
||||
// 3) 转换 + 异步回种
|
||||
rounds := sessionsToHistoryRounds(sessions)
|
||||
go asyncCacheToRedis(context.WithoutCancel(ctx), user.TenantId, req.SessionId, req.NodeId, rounds)
|
||||
|
||||
return &dto.GetHistoryMessagesRes{Messages: flattenRounds(rounds)}, nil
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 删除
|
||||
// ============================================
|
||||
|
||||
// DeleteMessages 删除消息
|
||||
func DeleteMessages(ctx context.Context, req *dto.DeleteMessagesReq) (*dto.DeleteMessagesRes, error) {
|
||||
if len(req.MsgIds) == 0 {
|
||||
return &dto.DeleteMessagesRes{Ok: false}, fmt.Errorf("msgIds不能为空")
|
||||
}
|
||||
|
||||
user, _ := utils.GetUserInfo(ctx)
|
||||
|
||||
// 1) 批量查询
|
||||
sessions, _ := dao.ComposeSession.ListByIds(ctx, req.MsgIds, user.UserName, req.SessionId)
|
||||
|
||||
// 2) 批量删 DB
|
||||
_, _ = dao.ComposeSession.DeleteByIds(ctx, req.MsgIds, user.UserName, req.SessionId)
|
||||
|
||||
// 3) 按 nodeId 分组删 Redis
|
||||
for _, s := range sessions {
|
||||
_ = DeleteRedisMessages(ctx, user.TenantId, req.SessionId, s.NodeId, req.MsgIds)
|
||||
}
|
||||
return &dto.DeleteMessagesRes{Ok: true}, nil
|
||||
}
|
||||
|
||||
// DeleteSession 删除整个会话
|
||||
func DeleteSession(ctx context.Context, req *dto.DeleteSessionReq) (*dto.DeleteSessionRes, error) {
|
||||
// 1) 删 DB
|
||||
if _, err := dao.ComposeSession.Delete(ctx, &entity.ComposeSession{
|
||||
SessionId: req.SessionId,
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("DB删除失败: %w", err)
|
||||
}
|
||||
|
||||
user, err := utils.GetUserInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 2) 删 Redis
|
||||
if err := DeleteSessionHistory(ctx, user.TenantId, req.SessionId); err != nil {
|
||||
g.Log().Warningf(ctx, "[删除会话] Redis删除失败 sessionId=%s err=%v", req.SessionId, err)
|
||||
}
|
||||
|
||||
return &dto.DeleteSessionRes{Ok: true}, nil
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 转换方法(entity ↔ dto,集中管理)
|
||||
// ============================================
|
||||
|
||||
// entityToHistoryRound entity → HistoryRound
|
||||
func entityToHistoryRound(s *entity.ComposeSession) *dto.HistoryRound {
|
||||
return &dto.HistoryRound{
|
||||
Id: s.Id,
|
||||
SessionId: s.SessionId,
|
||||
NodeId: s.NodeId,
|
||||
CreatedAt: gconv.String(s.CreatedAt),
|
||||
UpdatedAt: gconv.String(s.UpdatedAt),
|
||||
User: s.RequestContent,
|
||||
Assistant: s.ResponseContent,
|
||||
}
|
||||
}
|
||||
|
||||
// sessionsToHistoryRounds 批量转换
|
||||
func sessionsToHistoryRounds(sessions []*entity.ComposeSession) []dto.HistoryRound {
|
||||
rounds := make([]dto.HistoryRound, 0, len(sessions))
|
||||
for _, s := range sessions {
|
||||
rounds = append(rounds, *entityToHistoryRound(s))
|
||||
}
|
||||
return rounds
|
||||
}
|
||||
|
||||
// asyncCacheToRedis 异步缓存到 Redis
|
||||
func asyncCacheToRedis(ctx context.Context, tenantID uint64, sessionID, nodeID string, rounds []dto.HistoryRound) {
|
||||
for i := range rounds {
|
||||
if rounds[i].User != nil || rounds[i].Assistant != nil {
|
||||
_ = SaveToRedis(ctx, tenantID, sessionID, nodeID, &rounds[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
134
update.sql
Normal file
134
update.sql
Normal file
@@ -0,0 +1,134 @@
|
||||
-- prompts_compose_task 提示词任务记录表
|
||||
CREATE TABLE IF NOT EXISTS prompts_compose_task (
|
||||
id BIGINT PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0,
|
||||
creator VARCHAR(64) NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updater VARCHAR(64) NOT NULL,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP(6),
|
||||
|
||||
task_id VARCHAR(64) NOT NULL,
|
||||
model_name VARCHAR(128) NOT NULL DEFAULT '',
|
||||
skill_name VARCHAR(128) NOT NULL DEFAULT '',
|
||||
build_type INT NOT NULL DEFAULT 0,
|
||||
callback_url VARCHAR(512) NOT NULL DEFAULT '',
|
||||
gateway_state INT NOT NULL DEFAULT 0,
|
||||
request_payload JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
result_text TEXT NOT NULL DEFAULT '',
|
||||
messages JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
status VARCHAR(32) NOT NULL DEFAULT 'pending',
|
||||
error_message TEXT NOT NULL DEFAULT '',
|
||||
oss_file VARCHAR(1024) NOT NULL DEFAULT '',
|
||||
file_type VARCHAR(64) NOT NULL DEFAULT ''
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS uk_prompts_compose_task_task_id ON prompts_compose_task(task_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_prompts_compose_task_status ON prompts_compose_task(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_prompts_compose_task_deleted_at ON prompts_compose_task(deleted_at);
|
||||
|
||||
-- 注释
|
||||
COMMENT ON TABLE prompts_compose_task IS '提示词任务记录表';
|
||||
COMMENT ON COLUMN prompts_compose_task.id IS '主键ID';
|
||||
COMMENT ON COLUMN prompts_compose_task.tenant_id IS '租户ID';
|
||||
COMMENT ON COLUMN prompts_compose_task.creator IS '创建人';
|
||||
COMMENT ON COLUMN prompts_compose_task.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN prompts_compose_task.updater IS '更新人';
|
||||
COMMENT ON COLUMN prompts_compose_task.updated_at IS '更新时间';
|
||||
COMMENT ON COLUMN prompts_compose_task.deleted_at IS '删除时间(软删)';
|
||||
COMMENT ON COLUMN prompts_compose_task.task_id IS 'model-gateway 任务ID';
|
||||
COMMENT ON COLUMN prompts_compose_task.model_name IS '业务模型名称';
|
||||
COMMENT ON COLUMN prompts_compose_task.skill_name IS '技能名称';
|
||||
COMMENT ON COLUMN prompts_compose_task.build_type IS '构建类型:0默认/1提示词构建/2节点构建';
|
||||
COMMENT ON COLUMN prompts_compose_task.callback_url IS '回调地址';
|
||||
COMMENT ON COLUMN prompts_compose_task.gateway_state IS 'model-gateway 状态:0排队/1执行/2成功/3失败/4已下载';
|
||||
COMMENT ON COLUMN prompts_compose_task.request_payload IS '发给 model-gateway 的请求内容';
|
||||
COMMENT ON COLUMN prompts_compose_task.result_text IS '回调返回的文本结果';
|
||||
COMMENT ON COLUMN prompts_compose_task.messages IS '最终解析后的 messages';
|
||||
COMMENT ON COLUMN prompts_compose_task.status IS '业务状态:pending/success/failed';
|
||||
COMMENT ON COLUMN prompts_compose_task.error_message IS '业务错误信息';
|
||||
COMMENT ON COLUMN prompts_compose_task.oss_file IS '网关返回的结果文件地址';
|
||||
COMMENT ON COLUMN prompts_compose_task.file_type IS '结果文件类型';
|
||||
|
||||
|
||||
|
||||
-- prompts_compose_session 提示词历史会话表
|
||||
CREATE TABLE IF NOT EXISTS prompts_compose_session (
|
||||
id BIGINT NOT NULL,
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0,
|
||||
creator VARCHAR(64) NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updater VARCHAR(64) NOT NULL,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP(6),
|
||||
|
||||
session_id VARCHAR(64) NOT NULL,
|
||||
request_content JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
response_content JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
remark VARCHAR(500) NOT NULL DEFAULT ''
|
||||
);
|
||||
-- 索引
|
||||
CREATE INDEX IF NOT EXISTS idx_prompts_compose_session_session_id ON prompts_compose_session(session_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_prompts_compose_session_deleted_at ON prompts_compose_session(deleted_at);
|
||||
-- 注释
|
||||
COMMENT ON TABLE prompts_compose_session IS '提示词历史会话表';
|
||||
COMMENT ON COLUMN prompts_compose_session.id IS '主键ID';
|
||||
COMMENT ON COLUMN prompts_compose_session.tenant_id IS '租户ID';
|
||||
COMMENT ON COLUMN prompts_compose_session.creator IS '创建人';
|
||||
COMMENT ON COLUMN prompts_compose_session.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN prompts_compose_session.updater IS '更新人';
|
||||
COMMENT ON COLUMN prompts_compose_session.updated_at IS '更新时间';
|
||||
COMMENT ON COLUMN prompts_compose_session.deleted_at IS '删除时间(软删)';
|
||||
COMMENT ON COLUMN prompts_compose_session.session_id IS '会话ID';
|
||||
COMMENT ON COLUMN prompts_compose_session.request_content IS '请求内容(JSON格式)';
|
||||
COMMENT ON COLUMN prompts_compose_session.response_content IS '返回内容(JSON格式)';
|
||||
COMMENT ON COLUMN prompts_compose_session.remark IS '备注';
|
||||
|
||||
|
||||
|
||||
-- prompts_provider_protocol 模型协议映射配置表
|
||||
CREATE TABLE IF NOT EXISTS prompts_provider_protocol (
|
||||
id BIGINT PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0,
|
||||
creator VARCHAR(64) NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updater VARCHAR(64) NOT NULL,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMP(6),
|
||||
|
||||
provider_name VARCHAR(64) NOT NULL DEFAULT '',
|
||||
target_field VARCHAR(64) NOT NULL DEFAULT '',
|
||||
merge_order JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
role_mapping JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
content_mapping JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
capabilities JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
request_template JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
system_prompt_template TEXT NOT NULL DEFAULT '',
|
||||
user_prompt_template TEXT NOT NULL DEFAULT '',
|
||||
status INT NOT NULL DEFAULT 1,
|
||||
remark VARCHAR(500) NOT NULL DEFAULT ''
|
||||
);
|
||||
-- 索引
|
||||
CREATE INDEX IF NOT EXISTS idx_prompts_provider_protocol_provider_name ON prompts_provider_protocol(provider_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_prompts_provider_protocol_status ON prompts_provider_protocol(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_prompts_provider_protocol_deleted_at ON prompts_provider_protocol(deleted_at);
|
||||
-- 注释
|
||||
COMMENT ON TABLE prompts_provider_protocol IS '模型协议映射配置表';
|
||||
COMMENT ON COLUMN prompts_provider_protocol.id IS '主键ID';
|
||||
COMMENT ON COLUMN prompts_provider_protocol.tenant_id IS '租户ID';
|
||||
COMMENT ON COLUMN prompts_provider_protocol.creator IS '创建人';
|
||||
COMMENT ON COLUMN prompts_provider_protocol.created_at IS '创建时间';
|
||||
COMMENT ON COLUMN prompts_provider_protocol.updater IS '更新人';
|
||||
COMMENT ON COLUMN prompts_provider_protocol.updated_at IS '更新时间';
|
||||
COMMENT ON COLUMN prompts_provider_protocol.deleted_at IS '删除时间(软删)';
|
||||
COMMENT ON COLUMN prompts_provider_protocol.provider_name IS '运营商名称(openai/deepseek/qwen/anthropic/gemini等)';
|
||||
COMMENT ON COLUMN prompts_provider_protocol.target_field IS '目标字段(messages/contents/prompt)';
|
||||
COMMENT ON COLUMN prompts_provider_protocol.merge_order IS 'Prompt IR 拼接顺序(system/history/user)';
|
||||
COMMENT ON COLUMN prompts_provider_protocol.role_mapping IS '角色映射(system/user/assistant -> provider role)';
|
||||
COMMENT ON COLUMN prompts_provider_protocol.content_mapping IS '内容字段映射(content/parts.text等)';
|
||||
COMMENT ON COLUMN prompts_provider_protocol.capabilities IS '协议能力配置(system/history/tools/stream等支持情况)';
|
||||
COMMENT ON COLUMN prompts_provider_protocol.request_template IS '请求模板(JSON结构模板)';
|
||||
COMMENT ON COLUMN prompts_provider_protocol.system_prompt_template IS '系统提示词模板';
|
||||
COMMENT ON COLUMN prompts_provider_protocol.status IS '状态:1启用/0禁用';
|
||||
COMMENT ON COLUMN prompts_provider_protocol.remark IS '备注';
|
||||
Reference in New Issue
Block a user