Armored response parsing, dump failed analysis to a file

This commit is contained in:
Steve White 2025-01-26 14:37:29 -06:00
parent 72ba9ac98f
commit 78db506564
2 changed files with 46 additions and 7 deletions

View File

@ -9,6 +9,8 @@ Go package for automated evaluation of academic papers using LLM-based criteria
- Rate limiting with request delay configuration - Rate limiting with request delay configuration
- File-based processing (JSON input/output) - File-based processing (JSON input/output)
- Customizable evaluation criteria - Customizable evaluation criteria
- Robust error handling with failure tracking
- Automatic dump file for failed analyses
## Installation ## Installation
@ -19,7 +21,7 @@ go get gitea.r8z.us/stwhite/paperprocessor
## API Reference ## API Reference
### ProcessFile ### ProcessFile
`func ProcessFile(inputPath, outputPath, criteriaPath string, config Config, debug bool) error` `func ProcessFile(inputPath, outputPath, criteriaPath string, config Config) error`
Processes papers from input JSON file and writes results to output JSON file Processes papers from input JSON file and writes results to output JSON file
@ -28,7 +30,6 @@ Parameters:
- outputPath: Path to write processing results JSON - outputPath: Path to write processing results JSON
- criteriaPath: Path to text file with evaluation criteria - criteriaPath: Path to text file with evaluation criteria
- config: Configuration settings for API and processing - config: Configuration settings for API and processing
- debug: Enable debug logging when true
Returns: Returns:
- error: Processing error or nil if successful - error: Processing error or nil if successful
@ -54,7 +55,6 @@ err := paperprocessor.ProcessFile(
"output/results.json", "output/results.json",
"criteria.txt", "criteria.txt",
config, config,
true, // debug mode
) )
if err != nil { if err != nil {
log.Fatal("Processing failed:", err) log.Fatal("Processing failed:", err)
@ -106,10 +106,23 @@ Evaluation criteria:
"decision": "REJECT", "decision": "REJECT",
"explanation": "Doesn't meet novelty requirements..." "explanation": "Doesn't meet novelty requirements..."
} }
],
"failed": [
{
"paper": {
"title": "Problematic Paper",
"abstract": "...",
"arxiv_id": "2301.11111"
},
"error": "invalid decision format",
"output": ""
}
] ]
} }
``` ```
When papers fail processing, they are added to the "failed" list in the output JSON and also written to a `dump.json` file for detailed review.
## Configuration Options ## Configuration Options
| Parameter | Description | Default | | Parameter | Description | Default |

View File

@ -28,6 +28,11 @@ type PaperResult struct {
type ProcessingResult struct { type ProcessingResult struct {
Accepted []PaperResult `json:"accepted"` Accepted []PaperResult `json:"accepted"`
Rejected []PaperResult `json:"rejected"` Rejected []PaperResult `json:"rejected"`
Failed []struct {
Paper Paper `json:"paper"`
Error string `json:"error"`
Output string `json:"output"`
} `json:"failed"`
} }
// Config holds the configuration for the processor // Config holds the configuration for the processor
@ -79,7 +84,17 @@ func (p *Processor) ProcessPapers(papers []Paper, criteria string) (*ProcessingR
} }
decision, err := p.evaluatePaper(paper, criteria) decision, err := p.evaluatePaper(paper, criteria)
if err != nil { if err != nil {
return nil, fmt.Errorf("error processing paper %s: %v", paper.ArxivID, err) // Instead of returning error, add to failed list
result.Failed = append(result.Failed, struct {
Paper Paper `json:"paper"`
Error string `json:"error"`
Output string `json:"output"`
}{
Paper: paper,
Error: err.Error(),
Output: "", // We could potentially add the raw LLM output here if needed
})
continue
} }
paperResult := PaperResult{ paperResult := PaperResult{
@ -95,6 +110,14 @@ func (p *Processor) ProcessPapers(papers []Paper, criteria string) (*ProcessingR
} }
} }
// Write failed analyses to dump file if any exist
if len(result.Failed) > 0 {
dumpData, err := json.MarshalIndent(result.Failed, "", " ")
if err == nil { // Only try to write if marshaling succeeded
ioutil.WriteFile("dump.json", dumpData, 0644)
}
}
return result, nil return result, nil
} }
@ -123,8 +146,9 @@ type decisionResult struct {
func (p *Processor) evaluatePaper(paper Paper, criteria string) (*decisionResult, error) { func (p *Processor) evaluatePaper(paper Paper, criteria string) (*decisionResult, error) {
prompt := fmt.Sprintf(`Please evaluate the following academic paper against the provided criteria. prompt := fmt.Sprintf(`Please evaluate the following academic paper against the provided criteria.
Respond with either "ACCEPT" or "REJECT" followed by a brief explanation of your decision. Respond with either "ACCEPT" or "REJECT" followed by an explanation of your decision.
Do not use markdown emphasis in your response. Keep your response clear and concise. For ACCEPT decisions, provide a thorough explanation. For REJECT decisions, keep the explanation brief and focused on the key reason.
Do not use markdown, bullet points, or quotes in your response. Keep your response clear and concise.
Your response should be in the format: Your response should be in the format:
DECISION DECISION
Explanation Explanation
@ -201,10 +225,12 @@ Abstract: %s`, criteria, paper.Title, paper.Abstract)
// Handle common prefixes and clean the decision text // Handle common prefixes and clean the decision text
cleanDecision := rawDecision cleanDecision := rawDecision
for _, prefix := range []string{"DECISION:", "Decision:"} { for _, prefix := range []string{"DECISION:", "Decision:", "-", "\"", "*"} {
cleanDecision = strings.TrimPrefix(cleanDecision, prefix) cleanDecision = strings.TrimPrefix(cleanDecision, prefix)
} }
cleanDecision = strings.TrimSpace(cleanDecision) cleanDecision = strings.TrimSpace(cleanDecision)
// Remove any remaining quotes
cleanDecision = strings.Trim(cleanDecision, "\"")
// Normalize case // Normalize case
upperDecision := strings.ToUpper(cleanDecision) upperDecision := strings.ToUpper(cleanDecision)