3c5006e2f3
Propagate an optional imageName through the agent content pipeline (frontend types → API → domain → prompt building) so reference images render with their name instead of a bare "image" label. Also switch PromptComposer to submit on plain Enter (Shift+Enter for newline, IME-safe) and add a submitDisabled guard wired from the canvas and home composers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
598 lines
19 KiB
Go
598 lines
19 KiB
Go
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,
|
|
ImageName: content.ImageName,
|
|
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)
|
|
}
|