| package history |
|
|
| import ( |
| "context" |
| "errors" |
| "fmt" |
| "strings" |
|
|
| "ds2api/internal/auth" |
| "ds2api/internal/config" |
| dsclient "ds2api/internal/deepseek/client" |
| "ds2api/internal/httpapi/openai/shared" |
| "ds2api/internal/promptcompat" |
| ) |
|
|
| const ( |
| currentInputFilename = promptcompat.CurrentInputContextFilename |
| currentInputContentType = "text/plain; charset=utf-8" |
| currentInputPurpose = "assistants" |
| ) |
|
|
| type CurrentInputConfigReader interface { |
| CurrentInputFileEnabled() bool |
| CurrentInputFileMinChars() int |
| } |
|
|
| type CurrentInputUploader interface { |
| UploadFile(ctx context.Context, a *auth.RequestAuth, req dsclient.UploadFileRequest, maxAttempts int) (*dsclient.UploadFileResult, error) |
| } |
|
|
| type Service struct { |
| Store CurrentInputConfigReader |
| DS CurrentInputUploader |
| } |
|
|
| func (s Service) ApplyCurrentInputFile(ctx context.Context, a *auth.RequestAuth, stdReq promptcompat.StandardRequest) (promptcompat.StandardRequest, error) { |
| if stdReq.CurrentInputFileApplied || s.DS == nil || s.Store == nil || a == nil || !s.Store.CurrentInputFileEnabled() { |
| return stdReq, nil |
| } |
| threshold := s.Store.CurrentInputFileMinChars() |
|
|
| index, text := latestUserInputForFile(stdReq.Messages) |
| if index < 0 { |
| return stdReq, nil |
| } |
| if len([]rune(text)) < threshold { |
| return stdReq, nil |
| } |
| fileText := promptcompat.BuildOpenAICurrentInputContextTranscript(stdReq.Messages) |
| if strings.TrimSpace(fileText) == "" { |
| return stdReq, errors.New("current user input file produced empty transcript") |
| } |
| modelType := "default" |
| if resolvedType, ok := config.GetModelType(stdReq.ResolvedModel); ok { |
| modelType = resolvedType |
| } |
| result, err := s.DS.UploadFile(ctx, a, dsclient.UploadFileRequest{ |
| Filename: currentInputFilename, |
| ContentType: currentInputContentType, |
| Purpose: currentInputPurpose, |
| ModelType: modelType, |
| Data: []byte(fileText), |
| }, 3) |
| if err != nil { |
| return stdReq, fmt.Errorf("upload current user input file: %w", err) |
| } |
| fileID := strings.TrimSpace(result.ID) |
| if fileID == "" { |
| return stdReq, errors.New("upload current user input file returned empty file id") |
| } |
|
|
| messages := []any{ |
| map[string]any{ |
| "role": "user", |
| "content": currentInputFilePrompt(), |
| }, |
| } |
|
|
| stdReq.Messages = messages |
| stdReq.HistoryText = fileText |
| stdReq.CurrentInputFileApplied = true |
| stdReq.RefFileIDs = prependUniqueRefFileID(stdReq.RefFileIDs, fileID) |
| stdReq.FinalPrompt, stdReq.ToolNames = promptcompat.BuildOpenAIPrompt(messages, stdReq.ToolsRaw, "", stdReq.ToolChoice, stdReq.Thinking) |
| |
| |
| stdReq.PromptTokenText = fileText + "\n" + stdReq.FinalPrompt |
| return stdReq, nil |
| } |
|
|
| func latestUserInputForFile(messages []any) (int, string) { |
| for i := len(messages) - 1; i >= 0; i-- { |
| msg, ok := messages[i].(map[string]any) |
| if !ok { |
| continue |
| } |
| role := strings.ToLower(strings.TrimSpace(shared.AsString(msg["role"]))) |
| if role != "user" { |
| continue |
| } |
| text := promptcompat.NormalizeOpenAIContentForPrompt(msg["content"]) |
| if strings.TrimSpace(text) == "" { |
| return -1, "" |
| } |
| return i, text |
| } |
| return -1, "" |
| } |
|
|
| func currentInputFilePrompt() string { |
| return "Continue from the latest state in the attached DS2API_HISTORY.txt context. Treat it as the current working state and answer the latest user request directly." |
| } |
|
|
| func prependUniqueRefFileID(existing []string, fileID string) []string { |
| fileID = strings.TrimSpace(fileID) |
| if fileID == "" { |
| return existing |
| } |
| out := make([]string, 0, len(existing)+1) |
| out = append(out, fileID) |
| for _, id := range existing { |
| trimmed := strings.TrimSpace(id) |
| if trimmed == "" || strings.EqualFold(trimmed, fileID) { |
| continue |
| } |
| out = append(out, trimmed) |
| } |
| return out |
| } |
|
|