Initial commit: img-infinite-canvas AI design workbench MVP
Moteva-style AI design workbench replica. Home prompt creates a project; the project page provides an infinite canvas with node drag, zoom/pan, a design chat panel, history replay, image asset upload, and project save/regenerate. - Backend: go-zero, DDD layering, sqlc/pgx, optional PostgreSQL; memory or Redis cache; asynq job queue; MinIO/S3/R2/OSS object storage; sky-valley/pi agent runtime adapter. - Frontend: Next.js App Router + Vite artifact build, TypeScript, i18n, shadcn/ui components, auth (OTP/Turnstile/Google/WeChat). - Deploy: Docker Compose and k3s manifests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,596 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"img_infinite_canvas/internal/domain/design"
|
||||
"img_infinite_canvas/internal/types"
|
||||
)
|
||||
|
||||
func toProjectResponse(project design.Project) *types.ProjectResponse {
|
||||
return &types.ProjectResponse{Project: toAPIProject(project)}
|
||||
}
|
||||
|
||||
func toGeneratorTaskResponse(task design.GeneratorTask) *types.GeneratorTaskResponse {
|
||||
data := types.GeneratorTaskData{
|
||||
GeneratorTaskId: task.GeneratorTaskID,
|
||||
GeneratorName: task.GeneratorName,
|
||||
Status: task.Status,
|
||||
OriginalInputArgs: toAPIGeneratorTaskInputArgs(task.OriginalInputArgs),
|
||||
Artifacts: toAPIGeneratorTaskArtifacts(task.Artifacts),
|
||||
}
|
||||
if task.Status != "completed" {
|
||||
queueInfo := toAPIGeneratorTaskQueueInfo(task.QueueInfo)
|
||||
data.QueueInfo = &queueInfo
|
||||
}
|
||||
return &types.GeneratorTaskResponse{
|
||||
Code: 0,
|
||||
Message: "success",
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func toAPIGeneratorTaskInputArgs(input design.GeneratorTaskInputArgs) types.GeneratorTaskInputArgs {
|
||||
return types.GeneratorTaskInputArgs{
|
||||
AspectRatio: input.AspectRatio,
|
||||
Image: append([]string(nil), input.Image...),
|
||||
ImageUrl: input.ImageURL,
|
||||
Prompt: input.Prompt,
|
||||
Resolution: input.Resolution,
|
||||
SrcBox: toAPINormalizedBoxPtr(input.SrcBox),
|
||||
DstBox: toAPINormalizedBoxPtr(input.DstBox),
|
||||
}
|
||||
}
|
||||
|
||||
func toDomainGeneratorTaskInputArgs(input types.GeneratorTaskInputArgs) design.GeneratorTaskInputArgs {
|
||||
return design.GeneratorTaskInputArgs{
|
||||
AspectRatio: input.AspectRatio,
|
||||
Image: append([]string(nil), input.Image...),
|
||||
ImageURL: input.ImageUrl,
|
||||
Prompt: input.Prompt,
|
||||
Resolution: input.Resolution,
|
||||
SrcBox: toDomainNormalizedBoxPtr(input.SrcBox),
|
||||
DstBox: toDomainNormalizedBoxPtr(input.DstBox),
|
||||
}
|
||||
}
|
||||
|
||||
func toDomainNormalizedBox(box types.NormalizedBox) design.NormalizedBox {
|
||||
return design.NormalizedBox{
|
||||
X: box.X,
|
||||
Y: box.Y,
|
||||
Width: box.Width,
|
||||
Height: box.Height,
|
||||
}
|
||||
}
|
||||
|
||||
func toDomainNormalizedBoxPtr(box *types.NormalizedBox) *design.NormalizedBox {
|
||||
if box == nil {
|
||||
return nil
|
||||
}
|
||||
next := toDomainNormalizedBox(*box)
|
||||
return &next
|
||||
}
|
||||
|
||||
func toAPINormalizedBox(box design.NormalizedBox) types.NormalizedBox {
|
||||
return types.NormalizedBox{
|
||||
X: box.X,
|
||||
Y: box.Y,
|
||||
Width: box.Width,
|
||||
Height: box.Height,
|
||||
}
|
||||
}
|
||||
|
||||
func toAPINormalizedBoxPtr(box *design.NormalizedBox) *types.NormalizedBox {
|
||||
if box == nil {
|
||||
return nil
|
||||
}
|
||||
next := toAPINormalizedBox(*box)
|
||||
return &next
|
||||
}
|
||||
|
||||
func toRecognizeObjectResponse(result design.ObjectRecognition) *types.RecognizeObjectResponse {
|
||||
return &types.RecognizeObjectResponse{
|
||||
Id: result.ID,
|
||||
Label: result.Label,
|
||||
Confidence: result.Confidence,
|
||||
BBox: toAPINormalizedBox(result.BBox),
|
||||
}
|
||||
}
|
||||
|
||||
func toAPIGeneratorTaskQueueInfo(queue design.GeneratorTaskQueueInfo) types.GeneratorTaskQueueInfo {
|
||||
return types.GeneratorTaskQueueInfo{
|
||||
InQueue: queue.InQueue,
|
||||
Position: queue.Position,
|
||||
TotalQueueCount: queue.TotalQueueCount,
|
||||
EstimatedEndTime: queue.EstimatedEndTime,
|
||||
RemainingTimeSeconds: queue.RemainingTimeSeconds,
|
||||
DisableUnlimitedPrice: types.GeneratorTaskDisableUnlimitedPrice{
|
||||
Balance: queue.DisableUnlimitedPrice.Balance,
|
||||
Price: queue.DisableUnlimitedPrice.Price,
|
||||
DisableUnlimited: queue.DisableUnlimitedPrice.DisableUnlimited,
|
||||
PriceDetail: types.GeneratorTaskPriceDetail{
|
||||
BasePrice: queue.DisableUnlimitedPrice.PriceDetail.BasePrice,
|
||||
ExtInfoForUnitCount: queue.DisableUnlimitedPrice.PriceDetail.ExtInfoForUnitCount,
|
||||
GeneratorName: queue.DisableUnlimitedPrice.PriceDetail.GeneratorName,
|
||||
InputArgs: toAPIGeneratorTaskInputArgs(queue.DisableUnlimitedPrice.PriceDetail.InputArgs),
|
||||
OutputArgs: queue.DisableUnlimitedPrice.PriceDetail.OutputArgs,
|
||||
SearchKey: queue.DisableUnlimitedPrice.PriceDetail.SearchKey,
|
||||
TimeVariantPeriod: queue.DisableUnlimitedPrice.PriceDetail.TimeVariantPeriod,
|
||||
TotalPrice: queue.DisableUnlimitedPrice.PriceDetail.TotalPrice,
|
||||
UnitCount: queue.DisableUnlimitedPrice.PriceDetail.UnitCount,
|
||||
UnitName: queue.DisableUnlimitedPrice.PriceDetail.UnitName,
|
||||
UnitPrice: queue.DisableUnlimitedPrice.PriceDetail.UnitPrice,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func toAPIGeneratorTaskArtifacts(items []design.GeneratorTaskArtifact) []types.GeneratorTaskArtifact {
|
||||
resp := make([]types.GeneratorTaskArtifact, 0, len(items))
|
||||
for _, item := range items {
|
||||
resp = append(resp, types.GeneratorTaskArtifact{
|
||||
Type: item.Type,
|
||||
Content: item.Content,
|
||||
Metadata: types.GeneratorTaskArtifactMetadata{
|
||||
ArtifactId: item.Metadata.ArtifactID,
|
||||
Format: item.Metadata.Format,
|
||||
Height: item.Metadata.Height,
|
||||
Label: item.Metadata.Label,
|
||||
NodeId: item.Metadata.NodeID,
|
||||
NodeName: item.Metadata.NodeName,
|
||||
SizeBytes: item.Metadata.SizeBytes,
|
||||
Width: item.Metadata.Width,
|
||||
},
|
||||
})
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func toExtractNodeTextResponse(extraction design.TextExtraction) *types.ExtractNodeTextResponse {
|
||||
items := make([]types.ExtractedTextLayer, 0, len(extraction.Layers))
|
||||
for _, item := range extraction.Layers {
|
||||
originalText := item.OriginalText
|
||||
if originalText == "" {
|
||||
originalText = item.Text
|
||||
}
|
||||
items = append(items, types.ExtractedTextLayer{
|
||||
Text: item.Text,
|
||||
OriginalText: originalText,
|
||||
X: item.X,
|
||||
Y: item.Y,
|
||||
Width: item.Width,
|
||||
Height: item.Height,
|
||||
FontSize: item.FontSize,
|
||||
FontFamily: item.FontFamily,
|
||||
FontWeight: item.FontWeight,
|
||||
Color: item.Color,
|
||||
TextAlign: item.TextAlign,
|
||||
LineHeight: item.LineHeight,
|
||||
LetterSpacing: item.LetterSpacing,
|
||||
StrokeColor: item.StrokeColor,
|
||||
StrokeWidth: item.StrokeWidth,
|
||||
Opacity: item.Opacity,
|
||||
Rotation: item.Rotation,
|
||||
Confidence: item.Confidence,
|
||||
})
|
||||
}
|
||||
return &types.ExtractNodeTextResponse{Items: items}
|
||||
}
|
||||
|
||||
func toAPIProject(project design.Project) types.Project {
|
||||
return types.Project{
|
||||
Id: project.ID,
|
||||
UserId: project.UserID,
|
||||
Title: project.Title,
|
||||
Brief: project.Brief,
|
||||
Status: string(project.Status),
|
||||
Thumbnail: project.Thumbnail,
|
||||
UpdatedAt: formatTime(project.UpdatedAt),
|
||||
Version: project.Version,
|
||||
SnapshotId: project.SnapshotID,
|
||||
Canvas: project.Canvas,
|
||||
LastThreadId: project.LastThreadID,
|
||||
Viewport: toAPIView(project.Viewport),
|
||||
Nodes: toAPINodes(project.Nodes),
|
||||
Connections: toAPIConnections(project.Connections),
|
||||
Messages: toAPIMessages(project.Messages),
|
||||
}
|
||||
}
|
||||
|
||||
func toAPISummaries(items []design.ProjectSummary) []types.ProjectSummary {
|
||||
resp := make([]types.ProjectSummary, 0, len(items))
|
||||
for _, item := range items {
|
||||
resp = append(resp, types.ProjectSummary{
|
||||
Id: item.ID,
|
||||
UserId: item.UserID,
|
||||
Title: item.Title,
|
||||
Brief: item.Brief,
|
||||
Status: string(item.Status),
|
||||
Thumbnail: item.Thumbnail,
|
||||
UpdatedAt: formatTime(item.UpdatedAt),
|
||||
})
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func toAPIQuickActions(items []design.QuickAction) []types.QuickAction {
|
||||
resp := make([]types.QuickAction, 0, len(items))
|
||||
for _, item := range items {
|
||||
resp = append(resp, types.QuickAction{
|
||||
Id: item.ID,
|
||||
Label: item.Label,
|
||||
Prompt: item.Prompt,
|
||||
Icon: item.Icon,
|
||||
})
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func toAPIInspirations(items []design.InspirationItem) []types.InspirationItem {
|
||||
resp := make([]types.InspirationItem, 0, len(items))
|
||||
for _, item := range items {
|
||||
resp = append(resp, types.InspirationItem{
|
||||
Id: item.ID,
|
||||
Title: item.Title,
|
||||
Category: item.Category,
|
||||
Description: item.Description,
|
||||
Accent: item.Accent,
|
||||
})
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func toAPINodes(items []design.Node) []types.CanvasNode {
|
||||
resp := make([]types.CanvasNode, 0, len(items))
|
||||
for _, item := range items {
|
||||
resp = append(resp, types.CanvasNode{
|
||||
Id: item.ID,
|
||||
Type: string(item.Type),
|
||||
Title: item.Title,
|
||||
X: item.X,
|
||||
Y: item.Y,
|
||||
Width: item.Width,
|
||||
Height: item.Height,
|
||||
Content: item.Content,
|
||||
Tone: item.Tone,
|
||||
Status: item.Status,
|
||||
ParentId: item.ParentID,
|
||||
LayerRole: item.LayerRole,
|
||||
FontSize: item.FontSize,
|
||||
FontFamily: item.FontFamily,
|
||||
FontWeight: item.FontWeight,
|
||||
Color: item.Color,
|
||||
TextAlign: item.TextAlign,
|
||||
LineHeight: item.LineHeight,
|
||||
LetterSpacing: item.LetterSpacing,
|
||||
TextDecoration: item.TextDecoration,
|
||||
TextTransform: item.TextTransform,
|
||||
Opacity: item.Opacity,
|
||||
FillColor: item.FillColor,
|
||||
StrokeColor: item.StrokeColor,
|
||||
StrokeWidth: item.StrokeWidth,
|
||||
StrokeStyle: item.StrokeStyle,
|
||||
FlipX: item.FlipX,
|
||||
FlipY: item.FlipY,
|
||||
Rotation: item.Rotation,
|
||||
ImageAdjustments: item.ImageAdjustments,
|
||||
MockupSurface: toAPIMockupSurface(item.MockupSurface),
|
||||
MockupModel: toAPIMockupModel(item.MockupModel),
|
||||
MockupSourceContent: item.MockupSourceContent,
|
||||
})
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func toDomainNodes(items []types.CanvasNode) []design.Node {
|
||||
resp := make([]design.Node, 0, len(items))
|
||||
for _, item := range items {
|
||||
resp = append(resp, design.Node{
|
||||
ID: item.Id,
|
||||
Type: design.NodeType(item.Type),
|
||||
Title: item.Title,
|
||||
X: item.X,
|
||||
Y: item.Y,
|
||||
Width: item.Width,
|
||||
Height: item.Height,
|
||||
Content: item.Content,
|
||||
Tone: item.Tone,
|
||||
Status: item.Status,
|
||||
ParentID: item.ParentId,
|
||||
LayerRole: item.LayerRole,
|
||||
FontSize: item.FontSize,
|
||||
FontFamily: item.FontFamily,
|
||||
FontWeight: item.FontWeight,
|
||||
Color: item.Color,
|
||||
TextAlign: item.TextAlign,
|
||||
LineHeight: item.LineHeight,
|
||||
LetterSpacing: item.LetterSpacing,
|
||||
TextDecoration: item.TextDecoration,
|
||||
TextTransform: item.TextTransform,
|
||||
Opacity: item.Opacity,
|
||||
FillColor: item.FillColor,
|
||||
StrokeColor: item.StrokeColor,
|
||||
StrokeWidth: item.StrokeWidth,
|
||||
StrokeStyle: item.StrokeStyle,
|
||||
FlipX: item.FlipX,
|
||||
FlipY: item.FlipY,
|
||||
Rotation: item.Rotation,
|
||||
ImageAdjustments: item.ImageAdjustments,
|
||||
MockupSurface: toDomainMockupSurface(item.MockupSurface),
|
||||
MockupModel: toDomainMockupModel(item.MockupModel),
|
||||
MockupSourceContent: item.MockupSourceContent,
|
||||
})
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func toAPIMockupSurface(surface design.MockupSurface) types.MockupSurface {
|
||||
return types.MockupSurface{
|
||||
TargetId: surface.TargetID,
|
||||
Polygon: toAPIMockupPoints(surface.Polygon),
|
||||
Mesh: types.MockupMesh{
|
||||
Columns: int64(surface.Mesh.Columns),
|
||||
Rows: int64(surface.Mesh.Rows),
|
||||
Points: toAPIMockupPoints(surface.Mesh.Points),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func toDomainMockupSurface(surface types.MockupSurface) design.MockupSurface {
|
||||
return design.MockupSurface{
|
||||
TargetID: surface.TargetId,
|
||||
Polygon: toDomainMockupPoints(surface.Polygon),
|
||||
Mesh: design.MockupMesh{
|
||||
Columns: int(surface.Mesh.Columns),
|
||||
Rows: int(surface.Mesh.Rows),
|
||||
Points: toDomainMockupPoints(surface.Mesh.Points),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func toAPIMockupPlacement(placement design.MockupPlacement) types.MockupPlacement {
|
||||
return types.MockupPlacement{
|
||||
X: placement.X,
|
||||
Y: placement.Y,
|
||||
Width: placement.Width,
|
||||
Height: placement.Height,
|
||||
}
|
||||
}
|
||||
|
||||
func toAPIMockupModel(model design.MockupModel) types.MockupModel {
|
||||
return types.MockupModel{
|
||||
Version: int64(model.Version),
|
||||
DepthMap: toAPIMockupModelMap(model.DepthMap),
|
||||
DepthScaleMin: model.DepthScaleMin,
|
||||
DepthScaleMax: model.DepthScaleMax,
|
||||
ShadeMap: toAPIMockupModelMap(model.ShadeMap),
|
||||
SegmentationMap: toAPIMockupModelMap(model.SegmentationMap),
|
||||
XOffsetMap: toAPIMockupModelMap(model.XOffsetMap),
|
||||
YOffsetMap: toAPIMockupModelMap(model.YOffsetMap),
|
||||
CameraIntrinsics: types.MockupCameraIntrinsics{
|
||||
Matrix: append([]float64(nil), model.CameraIntrinsics.Matrix...),
|
||||
},
|
||||
ColorCorrection: model.ColorCorrection,
|
||||
ForegroundImage: model.ForegroundImage,
|
||||
}
|
||||
}
|
||||
|
||||
func toAPIMockupModelMap(modelMap design.MockupModelMap) types.MockupModelMap {
|
||||
return types.MockupModelMap{
|
||||
DataId: modelMap.DataID,
|
||||
Width: int64(modelMap.Width),
|
||||
Height: int64(modelMap.Height),
|
||||
Url: modelMap.URL,
|
||||
Data: append([]float64(nil), modelMap.Data...),
|
||||
}
|
||||
}
|
||||
|
||||
func toDomainMockupModel(model types.MockupModel) design.MockupModel {
|
||||
return design.MockupModel{
|
||||
Version: int(model.Version),
|
||||
DepthMap: toDomainMockupModelMap(model.DepthMap),
|
||||
DepthScaleMin: model.DepthScaleMin,
|
||||
DepthScaleMax: model.DepthScaleMax,
|
||||
ShadeMap: toDomainMockupModelMap(model.ShadeMap),
|
||||
SegmentationMap: toDomainMockupModelMap(model.SegmentationMap),
|
||||
XOffsetMap: toDomainMockupModelMap(model.XOffsetMap),
|
||||
YOffsetMap: toDomainMockupModelMap(model.YOffsetMap),
|
||||
CameraIntrinsics: design.MockupCameraIntrinsics{
|
||||
Matrix: append([]float64(nil), model.CameraIntrinsics.Matrix...),
|
||||
},
|
||||
ColorCorrection: model.ColorCorrection,
|
||||
ForegroundImage: model.ForegroundImage,
|
||||
}
|
||||
}
|
||||
|
||||
func toDomainMockupModelMap(modelMap types.MockupModelMap) design.MockupModelMap {
|
||||
return design.MockupModelMap{
|
||||
DataID: modelMap.DataId,
|
||||
Width: int(modelMap.Width),
|
||||
Height: int(modelMap.Height),
|
||||
URL: modelMap.Url,
|
||||
Data: append([]float64(nil), modelMap.Data...),
|
||||
}
|
||||
}
|
||||
|
||||
func toAPIMockupPoints(points []design.MockupPoint) []types.MockupPoint {
|
||||
if len(points) == 0 {
|
||||
return nil
|
||||
}
|
||||
resp := make([]types.MockupPoint, 0, len(points))
|
||||
for _, point := range points {
|
||||
resp = append(resp, types.MockupPoint{X: point.X, Y: point.Y})
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func toDomainMockupPoints(points []types.MockupPoint) []design.MockupPoint {
|
||||
if len(points) == 0 {
|
||||
return nil
|
||||
}
|
||||
resp := make([]design.MockupPoint, 0, len(points))
|
||||
for _, point := range points {
|
||||
resp = append(resp, design.MockupPoint{X: point.X, Y: point.Y})
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func toAPIConnections(items []design.Connection) []types.CanvasConnection {
|
||||
resp := make([]types.CanvasConnection, 0, len(items))
|
||||
for _, item := range items {
|
||||
resp = append(resp, types.CanvasConnection{
|
||||
Id: item.ID,
|
||||
FromNodeId: item.FromNodeID,
|
||||
ToNodeId: item.ToNodeID,
|
||||
})
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func toDomainConnections(items []types.CanvasConnection) []design.Connection {
|
||||
resp := make([]design.Connection, 0, len(items))
|
||||
for _, item := range items {
|
||||
resp = append(resp, design.Connection{
|
||||
ID: item.Id,
|
||||
FromNodeID: item.FromNodeId,
|
||||
ToNodeID: item.ToNodeId,
|
||||
})
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func toAPIMessages(items []design.Message) []types.AgentMessage {
|
||||
resp := make([]types.AgentMessage, 0, len(items))
|
||||
for _, item := range items {
|
||||
resp = append(resp, toAPIMessage(item))
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func isHiddenAgentMessage(item design.Message) bool {
|
||||
return item.Type == "text_extraction_result" || item.Name == "text_extraction" || item.Name == "canvas_action" || item.ToolHint == "canvas_action"
|
||||
}
|
||||
|
||||
func hiddenAgentThreadIDs(items []design.Message) map[string]bool {
|
||||
ids := make(map[string]bool)
|
||||
for _, item := range items {
|
||||
if item.ThreadID == "" || !isHiddenAgentMessage(item) {
|
||||
continue
|
||||
}
|
||||
ids[item.ThreadID] = true
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func toAPIMessage(item design.Message) types.AgentMessage {
|
||||
return types.AgentMessage{
|
||||
Id: item.ID,
|
||||
Role: item.Role,
|
||||
Type: item.Type,
|
||||
Title: item.Title,
|
||||
Content: item.Content,
|
||||
ThreadId: item.ThreadID,
|
||||
ActionId: item.ActionID,
|
||||
StepId: item.StepID,
|
||||
ToolCallId: item.ToolCallID,
|
||||
Name: item.Name,
|
||||
ToolHint: item.ToolHint,
|
||||
Status: item.Status,
|
||||
CreatedAt: formatTime(item.CreatedAt),
|
||||
}
|
||||
}
|
||||
|
||||
func toAPIView(item design.Viewport) types.CanvasViewport {
|
||||
return types.CanvasViewport{X: item.X, Y: item.Y, K: item.K}
|
||||
}
|
||||
|
||||
func toDomainView(item types.CanvasViewport) design.Viewport {
|
||||
return design.Viewport{X: item.X, Y: item.Y, K: item.K}
|
||||
}
|
||||
|
||||
func toDomainAgentChat(req *types.AgentChatRequest) design.AgentChatRequest {
|
||||
messages := make([]design.AgentChatMessage, 0, len(req.Messages))
|
||||
for _, message := range req.Messages {
|
||||
contents := make([]design.AgentContent, 0, len(message.Contents))
|
||||
for _, content := range message.Contents {
|
||||
contents = append(contents, design.AgentContent{
|
||||
Type: content.Type,
|
||||
Text: content.Text,
|
||||
TextSource: content.TextSource,
|
||||
ImageURL: content.ImageUrl,
|
||||
ImageWidth: content.ImageWidth,
|
||||
ImageHeight: content.ImageHeight,
|
||||
})
|
||||
}
|
||||
messages = append(messages, design.AgentChatMessage{
|
||||
Role: message.Role,
|
||||
Contents: contents,
|
||||
})
|
||||
}
|
||||
|
||||
return design.AgentChatRequest{
|
||||
Messages: messages,
|
||||
ToolConfig: design.AgentToolConfig{
|
||||
PreferToolCategories: map[string][]string{
|
||||
"SEARCH": req.ToolConfig.PreferToolCategories.Search,
|
||||
"IMAGE": req.ToolConfig.PreferToolCategories.Image,
|
||||
},
|
||||
},
|
||||
ThreadID: req.ThreadId,
|
||||
ThreadIDType: req.ThreadIdType,
|
||||
EnableMultimodalInput: req.EnableMultimodalInput,
|
||||
EnableWebSearch: req.EnableWebSearch || hasPreferredToolCategory(req.ToolConfig.PreferToolCategories.Search),
|
||||
ImageModel: req.ImageModel,
|
||||
ImageSize: req.ImageSize,
|
||||
Mode: req.Mode,
|
||||
}
|
||||
}
|
||||
|
||||
func hasPreferredToolCategory(items []string) bool {
|
||||
for _, item := range items {
|
||||
if item != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func toAPIAgentThread(thread design.AgentThread) *types.AgentThreadResponse {
|
||||
return &types.AgentThreadResponse{
|
||||
ThreadId: thread.ThreadID,
|
||||
Status: thread.Status,
|
||||
ThreadIdType: thread.ThreadIDType,
|
||||
EventsUrl: "/api/projects/" + thread.Project.ID + "/events?threadId=" + url.QueryEscape(thread.ThreadID),
|
||||
Project: toAPIProject(thread.Project),
|
||||
}
|
||||
}
|
||||
|
||||
func toAPIAgentThreadStatus(thread design.AgentThread) *types.AgentThreadStatusResponse {
|
||||
return &types.AgentThreadStatusResponse{
|
||||
ThreadId: thread.ThreadID,
|
||||
Status: thread.Status,
|
||||
ThreadIdType: thread.ThreadIDType,
|
||||
}
|
||||
}
|
||||
|
||||
func toAPIAgentThreadSummaries(items []design.AgentThreadSummary) []types.AgentThreadSummary {
|
||||
resp := make([]types.AgentThreadSummary, 0, len(items))
|
||||
for _, item := range items {
|
||||
resp = append(resp, types.AgentThreadSummary{
|
||||
AgentThreadId: item.AgentThreadID,
|
||||
CoverImageUrl: item.CoverImageURL,
|
||||
Text: item.Text,
|
||||
ThreadIdType: item.ThreadIDType,
|
||||
UpdateTimestamp: item.UpdateTimestamp,
|
||||
})
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func formatTime(value time.Time) string {
|
||||
if value.IsZero() {
|
||||
return ""
|
||||
}
|
||||
return value.UTC().Format(time.RFC3339)
|
||||
}
|
||||
Reference in New Issue
Block a user