Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
256 changes: 256 additions & 0 deletions agent/app/api/v2/ai.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
package v2

import (
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu"
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu/common"
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/xpu"
"github.com/gin-gonic/gin"
)

// @Tags AI
// @Summary Create Ollama model
// @Accept json
// @Param request body dto.OllamaModelName true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"添加 Ollama 模型 [name]","formatEN":"add Ollama model [name]"}
func (b *BaseApi) CreateOllamaModel(c *gin.Context) {
var req dto.OllamaModelName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}

if err := aiToolService.Create(req.Name); err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, nil)
}

// @Tags AI
// @Summary Rereate Ollama model
// @Accept json
// @Param request body dto.OllamaModelName true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model/recreate [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"添加 Ollama 模型重试 [name]","formatEN":"re-add Ollama model [name]"}
func (b *BaseApi) RecreateOllamaModel(c *gin.Context) {
var req dto.OllamaModelName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}

if err := aiToolService.Recreate(req.Name); err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, nil)
}

// @Tags AI
// @Summary Close Ollama model conn
// @Accept json
// @Param request body dto.OllamaModelName true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model/close [post]
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"关闭 Ollama 模型连接 [name]","formatEN":"close conn for Ollama model [name]"}
func (b *BaseApi) CloseOllamaModel(c *gin.Context) {
var req dto.OllamaModelName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}

if err := aiToolService.Close(req.Name); err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, nil)
}

// @Tags AI
// @Summary Sync Ollama model list
// @Success 200 {array} dto.OllamaModelDropList
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model/sync [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"同步 Ollama 模型列表","formatEN":"sync Ollama model list"}
func (b *BaseApi) SyncOllamaModel(c *gin.Context) {
list, err := aiToolService.Sync()
if err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, list)
}

// @Tags AI
// @Summary Page Ollama models
// @Accept json
// @Param request body dto.SearchWithPage true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model/search [post]
func (b *BaseApi) SearchOllamaModel(c *gin.Context) {
var req dto.SearchWithPage
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}

total, list, err := aiToolService.Search(req)
if err != nil {
helper.BadRequest(c, err)
return
}

helper.SuccessWithData(c, dto.PageResult{
Items: list,
Total: total,
})
}

// @Tags AI
// @Summary Page Ollama models
// @Accept json
// @Param request body dto.OllamaModelName true "request"
// @Success 200 {string} details
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model/load [post]
func (b *BaseApi) LoadOllamaModelDetail(c *gin.Context) {
var req dto.OllamaModelName
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}

detail, err := aiToolService.LoadDetail(req.Name)
if err != nil {
helper.BadRequest(c, err)
return
}

helper.SuccessWithData(c, detail)
}

// @Tags AI
// @Summary Delete Ollama model
// @Accept json
// @Param request body dto.ForceDelete true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/ollama/model/del [post]
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"ollama_models","output_column":"name","output_value":"names"}],"formatZH":"删除 Ollama 模型 [names]","formatEN":"remove Ollama model [names]"}
func (b *BaseApi) DeleteOllamaModel(c *gin.Context) {
var req dto.ForceDelete
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}

if err := aiToolService.Delete(req); err != nil {
helper.BadRequest(c, err)
return
}

helper.SuccessWithOutData(c)
}

// @Tags AI
// @Summary Load gpu / xpu info
// @Accept json
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/gpu/load [get]
func (b *BaseApi) LoadGpuInfo(c *gin.Context) {
ok, client := gpu.New()
if ok {
info, err := client.LoadGpuInfo()
if err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, info)
return
}
xpuOK, xpuClient := xpu.New()
if xpuOK {
info, err := xpuClient.LoadGpuInfo()
if err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, info)
return
}
helper.SuccessWithData(c, &common.GpuInfo{})
}

// @Tags AI
// @Summary Bind domain
// @Accept json
// @Param request body dto.OllamaBindDomain true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/domain/bind [post]
func (b *BaseApi) BindDomain(c *gin.Context) {
var req dto.OllamaBindDomain
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := aiToolService.BindDomain(req); err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithOutData(c)
}

// @Tags AI
// @Summary Get bind domain
// @Accept json
// @Param request body dto.OllamaBindDomainReq true "request"
// @Success 200 {object} dto.OllamaBindDomainRes
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /ai/domain/get [post]
func (b *BaseApi) GetBindDomain(c *gin.Context) {
var req dto.OllamaBindDomainReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
res, err := aiToolService.GetBindDomain(req)
if err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithData(c, res)
}

// Tags AI
// Summary Update bind domain
// Accept json
// Param request body dto.OllamaBindDomain true "request"
// Success 200
// Security ApiKeyAuth
// Security Timestamp
// Router /ai/domain/update [post]
func (b *BaseApi) UpdateBindDomain(c *gin.Context) {
var req dto.OllamaBindDomain
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := aiToolService.UpdateBindDomain(req); err != nil {
helper.BadRequest(c, err)
return
}
helper.SuccessWithOutData(c)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file appears to be part of an implementation of a service called "ai/ollama" which is used for operations related to AI models such as creating, recreating, closing models, syncing lists, and more. It's structured in a way that allows it to have dependencies on functions like CreateOllamaModel, RecreateOllamaModel, etc., with appropriate annotations indicating how these functions interact.

There doesn't seem to be immediate discrepancies or obvious issues; however, if you find any inconsistencies, anomalies, errors, or inefficiencies while looking at this codebase or any specific parts of its functionality and logic, please share them so we can analyze them together in order to better understand what might need improvement or fixing.
For optimization suggestions though, I would recommend focusing on efficiency improvements where necessary but maintaining the current structure and design decisions taken into consideration when building such comprehensive API implementations. Any unnecessary complexity should also be considered when making optimizations. If there are certain segments of this code that operate very much similar to each other and could potentially benefit from refactoring underlining their usage patterns, it might be useful. The key point is not changing things just for change sake, rather aiming for high-quality robustness, maintainability, readability, and scalability over time without sacrificing simplicity too much.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code you provided is for an API that interacts with a service for creating and managing different types of AI tools like Ollama models. The code looks mostly robust, but here are some potential optimizations:

  1. Remove unused imports at the start since they do not seem to be used anywhere within the code.
  2. Group common function calls together using import statements.

Here's what could look like after such adjustments:

package v2

import (
	"database/sql/driver"
	"encoding/json"

	"github.com/gin-gonic/gin"
)

// ...

Note: I made these comments based on their usage in context from your initial snippet. If additional contexts suggest otherwise (like if the commented lines were actually part of a specific use case outside this snippet), those should also be considered during review.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please provide the original code for comparison.

2 changes: 2 additions & 0 deletions agent/app/api/v2/entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ var (
appService = service.NewIAppService()
appInstallService = service.NewIAppInstalledService()

aiToolService = service.NewIAIToolService()

containerService = service.NewIContainerService()
composeTemplateService = service.NewIComposeTemplateService()
imageRepoService = service.NewIImageRepoService()
Expand Down
65 changes: 65 additions & 0 deletions agent/app/api/v2/terminal.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"time"

"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/service"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
"github.com/1Panel-dev/1Panel/agent/utils/terminal"
Expand Down Expand Up @@ -165,6 +166,70 @@ func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
}
}

func (b *BaseApi) OllamaWsSsh(c *gin.Context) {
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
global.LOG.Errorf("gin context http handler failed, err: %v", err)
return
}
defer wsConn.Close()

if global.CONF.Base.IsDemo {
if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) {
return
}
}

cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
return
}
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
return
}
name := c.Query("name")
if cmd.CheckIllegal(name) {
if wshandleError(wsConn, errors.New(" The command contains illegal characters.")) {
return
}
}
container, err := service.LoadContainerName()
if wshandleError(wsConn, errors.WithMessage(err, " load container name for ollama failed")) {
return
}
commands := []string{"ollama", "run", name}

pidMap := loadMapFromDockerTop(container)
fmt.Println("pidMap")
for k, v := range pidMap {
fmt.Println(k, v)
}
itemCmds := append([]string{"exec", "-it", container}, commands...)
slave, err := terminal.NewCommand(itemCmds)
if wshandleError(wsConn, err) {
return
}
defer killBash(container, strings.Join(commands, " "), pidMap)
defer slave.Close()

tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
if wshandleError(wsConn, err) {
return
}

quitChan := make(chan bool, 3)
tty.Start(quitChan)
go slave.Wait(quitChan)

<-quitChan

global.LOG.Info("websocket finished")
if wshandleError(wsConn, err) {
return
}
}

func wshandleError(ws *websocket.Conn, err error) bool {
if err != nil {
global.LOG.Errorf("handler ws faled:, err: %v", err)
Expand Down
44 changes: 44 additions & 0 deletions agent/app/dto/ai.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package dto

import "time"

type OllamaModelInfo struct {
ID uint `json:"id"`
Name string `json:"name"`
Size string `json:"size"`
From string `json:"from"`
LogFileExist bool `json:"logFileExist"`

Status string `json:"status"`
Message string `json:"message"`
CreatedAt time.Time `json:"createdAt"`
}

type OllamaModelDropList struct {
ID uint `json:"id"`
Name string `json:"name"`
}

type OllamaModelName struct {
Name string `json:"name"`
}

type OllamaBindDomain struct {
Domain string `json:"domain" validate:"required"`
AppInstallID uint `json:"appInstallID" validate:"required"`
SSLID uint `json:"sslID"`
WebsiteID uint `json:"websiteID"`
IPList string `json:"ipList"`
}

type OllamaBindDomainReq struct {
AppInstallID uint `json:"appInstallID" validate:"required"`
}

type OllamaBindDomainRes struct {
Domain string `json:"domain"`
SSLID uint `json:"sslID"`
AllowIPs []string `json:"allowIPs"`
WebsiteID uint `json:"websiteID"`
ConnUrl string `json:"connUrl"`
}
5 changes: 5 additions & 0 deletions agent/app/dto/common_req.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,8 @@ type UpdateGroup struct {
type OperateWithTask struct {
TaskID string `json:"taskID"`
}

type ForceDelete struct {
IDs []uint `json:"ids"`
ForceDelete bool `json:"forceDelete"`
}
Loading
Loading