This commit is contained in:
parent
f06f341d12
commit
644e1339fb
21
.editorconfig
Normal file
21
.editorconfig
Normal file
@ -0,0 +1,21 @@
|
||||
; https://editorconfig.org/
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[{Makefile,go.mod,go.sum,*.go,.gitmodules}]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
|
||||
[{*.yml,*.yaml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
57
.github/workflows/build.yaml
vendored
57
.github/workflows/build.yaml
vendored
@ -1,13 +1,6 @@
|
||||
on: [push, pull_request, workflow_dispatch]
|
||||
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- v*
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
@ -17,6 +10,9 @@ jobs:
|
||||
contents: write
|
||||
pull-requests: read
|
||||
checks: write
|
||||
env:
|
||||
DOCKER_REGISTRY: devops.lty.name
|
||||
DOCKER_IMAGE: ${{ github.repository }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@ -26,36 +22,33 @@ jobs:
|
||||
go-version: '1.23'
|
||||
- uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8
|
||||
with:
|
||||
version: v1.60
|
||||
- name: Build
|
||||
run: make -j8 all && tree bin
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: builds-${{ github.sha }}
|
||||
path: |
|
||||
bin/*.zip
|
||||
bin/*.tar.gz
|
||||
bin/*.sha256sum
|
||||
- name: Upload Release Asset
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
version: v1.63
|
||||
- name: build
|
||||
run: |
|
||||
gh release create ${GIT_TAG} bin/*.zip bin/*.tar.gz bin/*.sha256sum
|
||||
make build
|
||||
cp -r bin ${DISTNAME}
|
||||
cp -r agents ${DISTNAME}
|
||||
tar --zstd -cvf ${DISTNAME}.tar.zst ${DISTNAME}
|
||||
env:
|
||||
GIT_TAG: ${{ github.ref_name }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
DISTNAME: chatai-linux-amd64-${{ github.ref_name }}
|
||||
- if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: https://gitea.com/actions/release-action@21a5938ff2548f6472d89ba13b3cdd8af4c67068
|
||||
with:
|
||||
api_key: ${{ secrets.GITHUB_TOKEN }}
|
||||
files: |
|
||||
*.tar.zst
|
||||
- uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5
|
||||
- uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355
|
||||
registry: ${{ env.DOCKER_REGISTRY }}
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.PACKAGES_PUBLISH_TOKEN }}
|
||||
- if: startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'
|
||||
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm64,linux/riscv64,linux/386
|
||||
push: true
|
||||
tags: |
|
||||
ghcr.io/${{ github.repository }}:${{ startsWith(github.ref, 'refs/tags/') && 'latest' || 'dev' }}
|
||||
ghcr.io/${{ github.repository }}:${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || github.sha }}
|
||||
${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:${{ startsWith(github.ref, 'refs/tags/') && 'latest' || 'dev' }}
|
||||
${{ env.DOCKER_REGISTRY }}/${{ env.DOCKER_IMAGE }}:${{ startsWith(github.ref, 'refs/tags/') && github.ref_name || github.sha }}
|
||||
|
20
Dockerfile
20
Dockerfile
@ -1,13 +1,7 @@
|
||||
FROM --platform=$BUILDPLATFORM alpine:3.21 AS builder
|
||||
RUN apk add --no-cache tzdata ca-certificates
|
||||
|
||||
FROM alpine:3.21
|
||||
ARG TARGETPLATFORM
|
||||
COPY bin/arch/${TARGETPLATFORM}/ai-agent /app/ai-agent
|
||||
COPY agents /app/agents
|
||||
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
|
||||
COPY --from=builder /usr/share/ca-certificates /usr/share/ca-certificates
|
||||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
||||
WORKDIR /app
|
||||
EXPOSE 7120
|
||||
CMD ["/app/ai-agent"]
|
||||
FROM alpine:3.21
|
||||
COPY ./bin/chatai /app/chatai
|
||||
COPY ./agents /app/agents
|
||||
RUN apk add --no-cache tzdata ca-certificates
|
||||
WORKDIR /app
|
||||
EXPOSE 7120
|
||||
CMD ["/app/chatai"]
|
||||
|
61
Makefile
61
Makefile
@ -1,30 +1,10 @@
|
||||
#!/usr/bin/env make -f
|
||||
AGENTS_DIR = agents
|
||||
DIST_DIRECTORY = ./bin
|
||||
DIST_APPNAME = ai-agent
|
||||
DIST_EXENAME = ai-agent
|
||||
BUILDINFO_CLASS = github.com/ltylab/ai-agent/internal/conf
|
||||
BUILDINFO_VERSION := $(shell git describe --tags 2>/dev/null || echo "git/$(shell git rev-parse --short HEAD 2>/dev/null || echo 'none')")
|
||||
CROSS_BUILD_TRIPLES = darwin/amd64 \
|
||||
darwin/arm64 \
|
||||
windows/386 \
|
||||
windows/amd64 \
|
||||
windows/arm64 \
|
||||
linux/386 \
|
||||
linux/amd64 \
|
||||
linux/arm64 \
|
||||
linux/loong64 \
|
||||
linux/mips \
|
||||
linux/mips64 \
|
||||
linux/mips64le \
|
||||
linux/mipsle \
|
||||
linux/riscv64 \
|
||||
freebsd/386 \
|
||||
freebsd/amd64 \
|
||||
freebsd/arm64 \
|
||||
freebsd/riscv64
|
||||
OUTPUT := ./bin/
|
||||
VERSION := $(shell git describe --tags 2>/dev/null || echo "git/$(shell git rev-parse --short HEAD 2>/dev/null || echo 'none')")
|
||||
CGO_ENABLED := 0
|
||||
BUILDINFO_CLASS := devops.lty.name/luo/chatai/internal/config
|
||||
LDFLAGS = -s -w
|
||||
LDFLAGS += -X "$(BUILDINFO_CLASS).BuildVersion=$(BUILDINFO_VERSION)"
|
||||
LDFLAGS += -X "$(BUILDINFO_CLASS).BuildVersion=$(VERSION)"
|
||||
LDFLAGS += -X "$(BUILDINFO_CLASS).BuildTime=$(shell date '+%Y-%m-%d %H:%M:%S %Z')"
|
||||
LDFLAGS += -X "$(BUILDINFO_CLASS).BuildMode=release"
|
||||
|
||||
@ -36,7 +16,12 @@ build_debug: build
|
||||
|
||||
.PHONY: build
|
||||
build: clean install
|
||||
CGO_ENABLED=0 go build -ldflags '$(LDFLAGS)' -o $(DIST_DIRECTORY)/$(DIST_EXENAME)
|
||||
CGO_ENABLED=$(CGO_ENABLED) go build -ldflags '$(LDFLAGS)' -o $(OUTPUT)
|
||||
|
||||
|
||||
.PHONY: run
|
||||
run: install
|
||||
go run .
|
||||
|
||||
|
||||
.PHONY: install
|
||||
@ -46,26 +31,4 @@ install:
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rvf $(DIST_DIRECTORY)
|
||||
|
||||
|
||||
.PHONY: all $(CROSS_BUILD_TRIPLES)
|
||||
all: clean install $(CROSS_BUILD_TRIPLES)
|
||||
$(CROSS_BUILD_TRIPLES): GOOS = $(word 1,$(subst /, ,$@))
|
||||
$(CROSS_BUILD_TRIPLES): GOARCH = $(word 2,$(subst /, ,$@))
|
||||
$(CROSS_BUILD_TRIPLES): CROSS_DIST_DIRNAME = $(DIST_APPNAME)-$(subst /,.,$(BUILDINFO_VERSION)-$(GOOS)-$(GOARCH))
|
||||
$(CROSS_BUILD_TRIPLES): CROSS_DIST_EXENAME = $(DIST_EXENAME)$(if $(filter $(GOOS),windows),.exe,)
|
||||
$(CROSS_BUILD_TRIPLES): CROSS_DIST_ARCNAME = $(CROSS_DIST_DIRNAME).$(if $(filter $(GOOS),windows),zip,tar.gz)
|
||||
$(CROSS_BUILD_TRIPLES):
|
||||
$(MAKE) build GOOS=$(GOOS) GOARCH=$(GOARCH) \
|
||||
DIST_DIRECTORY=$(DIST_DIRECTORY)/$(CROSS_DIST_DIRNAME) \
|
||||
DIST_EXENAME=$(CROSS_DIST_EXENAME) && \
|
||||
cp -r $(AGENTS_DIR) $(DIST_DIRECTORY)/$(CROSS_DIST_DIRNAME)/ && \
|
||||
mkdir -p $(DIST_DIRECTORY)/arch/$(GOOS)/$(GOARCH) && \
|
||||
cp $(DIST_DIRECTORY)/$(CROSS_DIST_DIRNAME)/$(CROSS_DIST_EXENAME) $(DIST_DIRECTORY)/arch/$(GOOS)/$(GOARCH)/$(CROSS_DIST_EXENAME) && \
|
||||
if [ "$(GOOS)" = "windows" ]; then \
|
||||
cd $(DIST_DIRECTORY) && zip -r $(CROSS_DIST_ARCNAME) $(CROSS_DIST_DIRNAME) && cd -; \
|
||||
else \
|
||||
tar -cvzf $(DIST_DIRECTORY)/$(CROSS_DIST_ARCNAME) -C $(DIST_DIRECTORY) $(CROSS_DIST_DIRNAME); \
|
||||
fi && \
|
||||
sh -c "cd $(DIST_DIRECTORY) && sha256sum $(CROSS_DIST_ARCNAME) > $(CROSS_DIST_ARCNAME).sha256sum";
|
||||
rm -rvf $(OUTPUT)
|
||||
|
15
README.md
15
README.md
@ -1,35 +1,34 @@
|
||||
# Luo Tianyi Codelabs AI Agent
|
||||
# Chat AI
|
||||
|
||||
提供类似 QQ 界面的公共 AI 聊天机器人,基于 Google Gemini 模型。
|
||||
|
||||
**注意**
|
||||
- 不支持视频、图片、音频等多模态输入。
|
||||
- 完全公开、无频率限制与身份认证。建议使用免费的 API Key 并禁用结算账号。
|
||||
- 暂时没有列出所有模型的功能。
|
||||
|
||||
## 使用方法
|
||||
从 GitHub Release 下载最新版本的压缩包,解压缩。你可以使用 `-h` 参数查看帮助信息:
|
||||
从「发行版」下载最新版本的压缩包,解压缩。你可以使用 `-h` 参数查看帮助信息:
|
||||
```bash
|
||||
./ai-agent-web -h
|
||||
./chatai -h
|
||||
```
|
||||
|
||||
要正常启动服务器,您需要设置环境变量 `GOOGLE_AI_KEY`。您可以在 [Google AI Studio](https://aistudio.google.com) 免费获得一个(需要 Google 账号)。
|
||||
```bash
|
||||
export GOOGLE_AI_KEY=your-key
|
||||
./ai-agent-web
|
||||
./chatai
|
||||
```
|
||||
|
||||
您也可以使用 Docker 运行:
|
||||
```bash
|
||||
docker run -d -p 57120:7120 -e GOOGLE_AI_KEY=your-key \
|
||||
--name ai-agent-web ghcr.io/github.com/ltylab/ai-agent
|
||||
--name chatai lty.name/chatai
|
||||
```
|
||||
|
||||
如果需要自定义 AI Agent,请将您的 AI Agent 配置文件映射到 `/app/agents` 目录:
|
||||
```bash
|
||||
docker run -d -p 57120:7120 -e GOOGLE_AI_KEY=your-key \
|
||||
-v /path/to/your/agents:/app/agents \
|
||||
--name ai-agent-web ghcr.io/github.com/ltylab/ai-agent
|
||||
--name chatai lty.name/chatai
|
||||
```
|
||||
|
||||
可以通过 `-model` 参数修改模型,推荐使用下面两个免费模型:
|
||||
@ -55,6 +54,7 @@ agents
|
||||
"id": "luo",
|
||||
"name": "洛天依AI",
|
||||
"desc": "世界第一的 ∞ 吃货殿下~",
|
||||
"footer": "此 AI 并非洛天依官方提供,请勿用于商业用途。",
|
||||
"primaryColor": "#6cb6df",
|
||||
"secondaryColor": "#66ccff",
|
||||
"accentColor": "#a62f35"
|
||||
@ -65,6 +65,7 @@ agents
|
||||
- `id` 是 Agent ID,必须和文件夹名称一致。
|
||||
- `name` 是 Agent 名称,将显示在聊天界面。
|
||||
- `desc` 是 Agent 描述,将显示在聊天界面。
|
||||
- `footer` 是 Agent 页脚,用于显示版权信息、免责声明等。
|
||||
- `primaryColor` 是主色调,将用于聊天界面的背景色。可选,如果未设置则使用默认值。
|
||||
- `secondaryColor` 是次色调,将用于聊天界面的背景色。可选,如果未设置则使用默认值。
|
||||
- `accentColor` 是强调色,将用于聊天界面的背景色。可选,如果未设置则使用默认值。
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"id": "ldk",
|
||||
"name": "李迪克AI",
|
||||
"desc": "我跟你讲!我家星尘宝宝,可爱(*´▽`*)~"
|
||||
"desc": "我跟你讲!我家星尘宝宝,可爱(*´▽`*)~",
|
||||
"footer": "AI生成的内容不代表 <a href=\"https://space.bilibili.com/882467\" target=\"_blank\" rel=\"noopener noreferrer\">李迪克</a> 的观点 | 数据来源: <a href=\"https://cyberldk.com\" target=\"_blank\" rel=\"noopener noreferrer\">CyberLDK</a>"
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
"id": "luo",
|
||||
"name": "洛天依AI",
|
||||
"desc": "世界第一的 ∞ 吃货殿下~",
|
||||
"footer": "此 AI 并非官方制作 | 请勿用于不当或商业用途",
|
||||
"primaryColor": "#6cb6df",
|
||||
"secondaryColor": "#66ccff",
|
||||
"accentColor": "#a62f35"
|
||||
|
2
go.mod
2
go.mod
@ -1,4 +1,4 @@
|
||||
module github.com/ltylab/ai-agent
|
||||
module devops.lty.name/luo/chatai
|
||||
|
||||
go 1.23.3
|
||||
|
||||
|
@ -7,9 +7,9 @@ import (
|
||||
"path"
|
||||
"regexp"
|
||||
|
||||
"github.com/ltylab/ai-agent/internal/conf"
|
||||
"github.com/ltylab/ai-agent/internal/log"
|
||||
"github.com/ltylab/ai-agent/web/assets"
|
||||
"devops.lty.name/luo/chatai/internal/config"
|
||||
"devops.lty.name/luo/chatai/internal/log"
|
||||
"devops.lty.name/luo/chatai/web/assets"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
@ -24,6 +24,9 @@ type AgentConf struct {
|
||||
// AgentDescription is a short description of the agent.
|
||||
AgentDescription string `json:"desc"`
|
||||
|
||||
// FooterHTML is the HTML content to be displayed in the footer of the agent chat page.
|
||||
FooterHTML string `json:"footer"`
|
||||
|
||||
// PrimaryColor is the primary color for the agent chat page.
|
||||
PrimaryColor string `json:"primaryColor"`
|
||||
|
||||
@ -78,11 +81,11 @@ func (a *AgentConf) ReadAgent() *Agent {
|
||||
func LoadAgentFromFile(agentJSONPath string) (*AgentConf, error) {
|
||||
conf := &AgentConf{
|
||||
AgentID: uuid.New().String(),
|
||||
AgentName: conf.DefaultAgentName,
|
||||
AgentDescription: conf.DefaultAgentDescription,
|
||||
PrimaryColor: conf.DefaultAgentPrimaryColor,
|
||||
SecondaryColor: conf.DefaultAgentSecondaryColor,
|
||||
AccentColor: conf.DefaultAgentAccentColor,
|
||||
AgentName: config.DefaultAgentName,
|
||||
AgentDescription: config.DefaultAgentDescription,
|
||||
PrimaryColor: config.DefaultAgentPrimaryColor,
|
||||
SecondaryColor: config.DefaultAgentSecondaryColor,
|
||||
AccentColor: config.DefaultAgentAccentColor,
|
||||
Dir: path.Dir(agentJSONPath),
|
||||
}
|
||||
|
||||
|
@ -1,22 +0,0 @@
|
||||
package conf
|
||||
|
||||
var (
|
||||
BuildVersion = "dev"
|
||||
BuildTime = "<unknown>"
|
||||
BuildMode = "development"
|
||||
PackageLicense = "Unlicense"
|
||||
PackageCopyright = "This is an unlicensed software (under public domain) by Luo Tianyi Codelabs <https://lty.name/>."
|
||||
|
||||
DefaultListen = ":7120"
|
||||
DefaultAgentsDir = "agents"
|
||||
DefaultLogLevel = "dbg"
|
||||
DefaultMsgLogPath = "logs"
|
||||
|
||||
DefaultAgentName = "未名"
|
||||
DefaultAgentDescription = "这个 Agent 没有名字"
|
||||
DefaultAgentPrimaryColor = "#444e8d"
|
||||
DefaultAgentSecondaryColor = "#9f9ff5"
|
||||
DefaultAgentAccentColor = "#eeaf5b"
|
||||
|
||||
DefaultGoogleAIModel = "gemini-1.5-flash"
|
||||
)
|
21
internal/config/conf.go
Normal file
21
internal/config/conf.go
Normal file
@ -0,0 +1,21 @@
|
||||
package config
|
||||
|
||||
var (
|
||||
BuildVersion = "dev"
|
||||
BuildTime = "<unknown>"
|
||||
BuildMode = "development"
|
||||
Copyright = "This is an unlicensed software (under public domain) by Luo Tianyi Codelabs <https://lty.name/>."
|
||||
|
||||
FlagListen = ":7120"
|
||||
FlagAgentsDir = "agents"
|
||||
FlagLogLevel = "dbg"
|
||||
FlagMsgLogPath = "logs"
|
||||
|
||||
DefaultAgentName = "未名"
|
||||
DefaultAgentDescription = "这个 Agent 没有名字"
|
||||
DefaultAgentPrimaryColor = "#444e8d"
|
||||
DefaultAgentSecondaryColor = "#9f9ff5"
|
||||
DefaultAgentAccentColor = "#eeaf5b"
|
||||
|
||||
DefaultGoogleAIModel = "gemini-1.5-flash"
|
||||
)
|
@ -8,8 +8,8 @@ import (
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/ltylab/ai-agent/internal/agent"
|
||||
"github.com/ltylab/ai-agent/internal/log"
|
||||
"devops.lty.name/luo/chatai/internal/agent"
|
||||
"devops.lty.name/luo/chatai/internal/log"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/generative-ai-go/genai"
|
||||
|
@ -1,15 +1,15 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"text/template"
|
||||
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/ltylab/ai-agent/internal/agent"
|
||||
"github.com/ltylab/ai-agent/internal/log"
|
||||
"github.com/ltylab/ai-agent/web"
|
||||
"github.com/ltylab/ai-agent/web/assets"
|
||||
"devops.lty.name/luo/chatai/internal/agent"
|
||||
"devops.lty.name/luo/chatai/internal/log"
|
||||
"devops.lty.name/luo/chatai/web"
|
||||
"devops.lty.name/luo/chatai/web/assets"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@ -38,11 +38,32 @@ func Setup(e *gin.Engine, agentsDir string, msgLogPath string) {
|
||||
}
|
||||
|
||||
e.StaticFS("/assets", http.FS(assets.AssetsFS))
|
||||
e.GET("", s.handleHomepageWithRendenedTemplate)
|
||||
e.GET("/:agentID", s.handleWithRendenedTemplate)
|
||||
e.GET("/:agentID/avatar.webp", s.handleAvatar)
|
||||
e.POST("/:agentID/chat", s.handleChat)
|
||||
}
|
||||
|
||||
func (s *Server) handleHomepageWithRendenedTemplate(c *gin.Context) {
|
||||
tmpl, err := template.New("home").Parse(web.HomeLayout)
|
||||
if err != nil {
|
||||
log.T("server/tmpl").Errf("Failed to parse template: %v", err)
|
||||
c.String(http.StatusInternalServerError, "server error")
|
||||
return
|
||||
}
|
||||
|
||||
err = tmpl.Execute(c.Writer, s.agents)
|
||||
if err != nil {
|
||||
log.T("server/tmpl").Errf("Failed to render template: %v", err)
|
||||
c.String(http.StatusInternalServerError, "server error")
|
||||
return
|
||||
}
|
||||
c.Status(http.StatusOK)
|
||||
c.Header("Content-Type", "text/html; charset=utf-8")
|
||||
c.Writer.Flush()
|
||||
c.Abort()
|
||||
}
|
||||
|
||||
func (s *Server) handleWithRendenedTemplate(c *gin.Context) {
|
||||
agentID := c.Param("agentID")
|
||||
agentConf, ok := s.agents[agentID]
|
||||
@ -51,7 +72,7 @@ func (s *Server) handleWithRendenedTemplate(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
tmpl, err := template.New("tmpl.html").Parse(web.Template)
|
||||
tmpl, err := template.New("agent").Parse(web.AgentLayout)
|
||||
if err != nil {
|
||||
log.T("server/tmpl").Errf("Failed to parse template: %v", err)
|
||||
c.String(http.StatusInternalServerError, "server error")
|
||||
|
22
main.go
22
main.go
@ -4,19 +4,19 @@ import (
|
||||
"flag"
|
||||
"os"
|
||||
|
||||
"github.com/ltylab/ai-agent/internal/conf"
|
||||
"github.com/ltylab/ai-agent/internal/log"
|
||||
"github.com/ltylab/ai-agent/internal/server"
|
||||
"devops.lty.name/luo/chatai/internal/config"
|
||||
"devops.lty.name/luo/chatai/internal/log"
|
||||
"devops.lty.name/luo/chatai/internal/server"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func main() {
|
||||
listen := flag.String("listen", conf.DefaultListen, "http listen address")
|
||||
agentsDir := flag.String("agents", conf.DefaultAgentsDir, "directory to store agent configurations")
|
||||
logLevel := flag.String("level", conf.DefaultLogLevel, "log level (dbg, inf, wrn, err)")
|
||||
msgLogPath := flag.String("msglog", conf.DefaultMsgLogPath, "audit log path")
|
||||
model := flag.String("model", conf.DefaultGoogleAIModel, "Google AI model (gemini-1.5-flash, gemini-2.0-flash-exp)")
|
||||
listen := flag.String("listen", config.FlagListen, "http listen address")
|
||||
agentsDir := flag.String("agents", config.FlagAgentsDir, "directory to store agent configurations")
|
||||
logLevel := flag.String("level", config.FlagLogLevel, "log level (dbg, inf, wrn, err)")
|
||||
msgLogPath := flag.String("msglog", config.FlagMsgLogPath, "audit log path")
|
||||
model := flag.String("model", config.DefaultGoogleAIModel, "Google AI model (gemini-1.5-flash, gemini-2.0-flash-exp)")
|
||||
flag.Parse()
|
||||
|
||||
server.GoogleAIKey = os.Getenv("GOOGLE_AI_KEY")
|
||||
@ -27,11 +27,11 @@ func main() {
|
||||
server.GoogleAIModel = *model
|
||||
|
||||
log.Setup(log.LogLevel(log.ParseLogLevel(*logLevel)))
|
||||
log.T("main").Inff("AI-Agent version %s, %s mode (build %s)", conf.BuildVersion, conf.BuildMode, conf.BuildTime)
|
||||
log.T("main").Inff("%s", conf.PackageCopyright)
|
||||
log.T("main").Inff("AI-Agent version %s, %s mode (build %s)", config.BuildVersion, config.BuildMode, config.BuildTime)
|
||||
log.T("main").Inff("%s", config.Copyright)
|
||||
|
||||
log.SetupGin1()
|
||||
if conf.BuildMode == "release" {
|
||||
if config.BuildMode == "release" {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
}
|
||||
|
||||
|
@ -248,7 +248,7 @@
|
||||
<aside>
|
||||
<button>_</button>
|
||||
<button>□</button>
|
||||
<button>×</button>
|
||||
<button onclick="location.href = '/'">×</button>
|
||||
</aside>
|
||||
</header>
|
||||
<article>
|
||||
@ -264,7 +264,8 @@
|
||||
</footer>
|
||||
</main>
|
||||
<div class="copy">
|
||||
以上内容为 AI 生成,请注意辨别。<br>为了安全和审计需要,我们可能记录您与 AI 的对话内容和您的 IP 地址。请勿向模型分享敏感信息。
|
||||
{{.FooterHTML}}<br>
|
||||
以上内容为 AI 生成,请注意辨别。<br>为了安全和审计需要,我们可能记录您与 AI 的对话内容和您的 IP 地址。请勿向 AI 分享敏感信息。
|
||||
</div>
|
||||
<template id="chat-record">
|
||||
<section>
|
@ -4,5 +4,8 @@ import (
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
//go:embed tmpl.html
|
||||
var Template string
|
||||
//go:embed home.html
|
||||
var HomeLayout string
|
||||
|
||||
//go:embed agent.html
|
||||
var AgentLayout string
|
||||
|
150
web/home.html
Normal file
150
web/home.html
Normal file
@ -0,0 +1,150 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="Cache-Control" content="no-siteapp">
|
||||
<meta name="google" content="notranslate">
|
||||
<link rel="icon" type="image/png" href="/assets/logo.png">
|
||||
<title>选择一个 AI Agent 开始聊天</title>
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: -apple-system, 'Helvetica Neue', Helvetica, 'Nimbus Sans L', Arial, 'Liberation Sans',
|
||||
'PingFang SC', 'Hiragino Sans GB', 'Source Han Sans CN', 'Source Han Sans SC', 'Microsoft YaHei', 'Wenquanyi Micro Hei', 'WenQuanYi Zen Hei',
|
||||
'ST Heiti', SimHei, 'WenQuanYi Zen Hei Sharp', sans-serif;
|
||||
background-color: #f0f0f0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
margin-bottom: 3rem;
|
||||
font-size: 1.8rem;
|
||||
color: #3c3c3c;
|
||||
text-align: center;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
main {
|
||||
width: 100%;
|
||||
height: 40%;
|
||||
min-height: 400px;
|
||||
flex-wrap: nowrap;
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
main .container {
|
||||
height: 100%;
|
||||
padding: 1rem;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
overflow-x: auto;
|
||||
gap: 2.5rem;
|
||||
justify-content: space-evenly;
|
||||
align-items: center;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
main .container::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
section {
|
||||
height: 100%;
|
||||
background: #fcfcfc;
|
||||
min-width: 300px;
|
||||
max-width: 300px;
|
||||
padding: 1rem;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 0 1rem rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
section img {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1.5rem;
|
||||
width: 3.5rem;
|
||||
height: 3.5rem;
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
section h2 {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 400;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
section span {
|
||||
font-size: 0.8rem;
|
||||
color: #666;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
section a {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: 0.5rem;
|
||||
text-decoration: none;
|
||||
transition: all 0.3s;
|
||||
justify-self: flex-end;
|
||||
cursor: pointer;
|
||||
margin-bottom: .5rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
{{range .}}
|
||||
.chat-btn__{{.AgentID}} {
|
||||
background: {{.PrimaryColor}};
|
||||
color: white;
|
||||
}
|
||||
|
||||
.chat-btn__{{.AgentID}}:hover {
|
||||
background: {{.SecondaryColor}};
|
||||
}
|
||||
{{end}}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>选择一个 AI Agent 开始聊天</h1>
|
||||
<main>
|
||||
<div class="container">
|
||||
{{range .}}
|
||||
<section>
|
||||
<div>
|
||||
<img src="/{{.AgentID}}/avatar.webp" alt="{{.AgentName}} 头像">
|
||||
<h2>{{.AgentName}}</h2>
|
||||
<span>{{.AgentDescription}}</span>
|
||||
</div>
|
||||
<a href="/{{.AgentID}}" class="chat-btn__{{.AgentID}}">开始聊天</a>
|
||||
</section>
|
||||
{{end}}
|
||||
</div>
|
||||
</main>
|
||||
<script>
|
||||
window.addEventListener("wheel", function (e) {
|
||||
const container = document.querySelector(".container");
|
||||
if (e.deltaY > 0) container.scrollLeft += 100;
|
||||
else container.scrollLeft -= 100;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user