( ctx context.Context, db database.Store, parts []codersdk.ChatInputPart, fieldName string, )
| 6212 | } |
| 6213 | |
| 6214 | func createChatInputFromParts( |
| 6215 | ctx context.Context, |
| 6216 | db database.Store, |
| 6217 | parts []codersdk.ChatInputPart, |
| 6218 | fieldName string, |
| 6219 | ) ([]codersdk.ChatMessagePart, string, []uuid.UUID, *codersdk.Response) { |
| 6220 | if len(parts) == 0 { |
| 6221 | return nil, "", nil, &codersdk.Response{ |
| 6222 | Message: "Content is required.", |
| 6223 | Detail: "Content cannot be empty.", |
| 6224 | } |
| 6225 | } |
| 6226 | |
| 6227 | var fileIDs []uuid.UUID |
| 6228 | content := make([]codersdk.ChatMessagePart, 0, len(parts)) |
| 6229 | textParts := make([]string, 0, len(parts)) |
| 6230 | for i, part := range parts { |
| 6231 | switch strings.ToLower(strings.TrimSpace(string(part.Type))) { |
| 6232 | case string(codersdk.ChatInputPartTypeText): |
| 6233 | text := strings.TrimSpace(part.Text) |
| 6234 | if text == "" { |
| 6235 | return nil, "", nil, &codersdk.Response{ |
| 6236 | Message: "Invalid input part.", |
| 6237 | Detail: fmt.Sprintf("%s[%d].text cannot be empty.", fieldName, i), |
| 6238 | } |
| 6239 | } |
| 6240 | content = append(content, codersdk.ChatMessageText(text)) |
| 6241 | textParts = append(textParts, text) |
| 6242 | case string(codersdk.ChatInputPartTypeFile): |
| 6243 | if part.FileID == uuid.Nil { |
| 6244 | return nil, "", nil, &codersdk.Response{ |
| 6245 | Message: "Invalid input part.", |
| 6246 | Detail: fmt.Sprintf("%s[%d].file_id is required for file parts.", fieldName, i), |
| 6247 | } |
| 6248 | } |
| 6249 | // Validate that the file exists and get its media type. |
| 6250 | // File data is not loaded here; it's resolved at LLM |
| 6251 | // dispatch time via chatFileResolver. |
| 6252 | chatFile, err := db.GetChatFileByID(ctx, part.FileID) |
| 6253 | if err != nil { |
| 6254 | if httpapi.Is404Error(err) { |
| 6255 | return nil, "", nil, &codersdk.Response{ |
| 6256 | Message: "Invalid input part.", |
| 6257 | Detail: fmt.Sprintf("%s[%d].file_id references a file that does not exist.", fieldName, i), |
| 6258 | } |
| 6259 | } |
| 6260 | return nil, "", nil, &codersdk.Response{ |
| 6261 | Message: "Internal error.", |
| 6262 | Detail: fmt.Sprintf("Failed to retrieve file for %s[%d].", fieldName, i), |
| 6263 | } |
| 6264 | } |
| 6265 | content = append(content, codersdk.ChatMessageFile(part.FileID, chatFile.Mimetype, chatFile.Name)) |
| 6266 | fileIDs = append(fileIDs, part.FileID) |
| 6267 | // file-reference parts carry inline code snippets, not uploaded |
| 6268 | // files. They have no FileID and are excluded from file tracking. |
| 6269 | case string(codersdk.ChatInputPartTypeFileReference): |
| 6270 | if part.FileName == "" { |
| 6271 | return nil, "", nil, &codersdk.Response{ |
no test coverage detected