Compare commits

..

5 Commits

Author SHA1 Message Date
Steve White 0d44120a63 Updated README.md 2025-01-26 00:25:55 -06:00
Steve White 171698a56b Updating message 2025-01-25 22:03:05 -06:00
Steve White 698e7caa6f Updated message 2025-01-25 20:59:28 -06:00
Steve White 4c2adc636b More armoring of decision parsing. 2025-01-25 20:56:23 -06:00
Steve White 36fdd5f00e Armored the decision parsing. 2025-01-25 20:51:37 -06:00
2 changed files with 59 additions and 12 deletions

View File

@ -16,6 +16,23 @@ Go package for automated evaluation of academic papers using LLM-based criteria
go get gitea.r8z.us/stwhite/paperprocessor
```
## API Reference
### ProcessFile
`func ProcessFile(inputPath, outputPath, criteriaPath string, config Config, debug bool) error`
Processes papers from input JSON file and writes results to output JSON file
Parameters:
- inputPath: Path to input JSON file containing papers array
- outputPath: Path to write processing results JSON
- criteriaPath: Path to text file with evaluation criteria
- config: Configuration settings for API and processing
- debug: Enable debug logging when true
Returns:
- error: Processing error or nil if successful
## Usage
### Basic Configuration
@ -37,7 +54,11 @@ err := paperprocessor.ProcessFile(
"output/results.json",
"criteria.txt",
config,
true, // debug mode
)
if err != nil {
log.Fatal("Processing failed:", err)
}
```
## Input Formats

View File

@ -8,6 +8,7 @@ import (
"net/http"
"strings"
"time"
"unicode"
)
// Paper represents a single academic paper
@ -124,6 +125,7 @@ type decisionResult struct {
func (p *Processor) evaluatePaper(paper Paper, criteria string) (*decisionResult, error) {
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.
Do not use markdown emphasis in your response. Keep your response clear and concise.
Your response should be in the format:
DECISION
Explanation
@ -179,22 +181,46 @@ Abstract: %s`, criteria, paper.Title, paper.Abstract)
}
content := llmResp.Choices[0].Message.Content
// Find first line with ACCEPT/REJECT
var decisionLine string
lines := bytes.Split([]byte(content), []byte("\n"))
if len(lines) < 2 {
return nil, fmt.Errorf("invalid response format")
for _, line := range lines {
if strings.Contains(strings.ToUpper(string(line)), "ACCEPT") ||
strings.Contains(strings.ToUpper(string(line)), "REJECT") {
decisionLine = string(line)
break
}
}
decisionParts := bytes.SplitN(lines[0], []byte(":"), 2)
if len(decisionParts) != 2 {
return nil, fmt.Errorf("invalid decision format, expected 'DECISION: [VALUE]'")
}
decision := strings.ToUpper(string(bytes.TrimSpace(decisionParts[1])))
if decision != "ACCEPT" && decision != "REJECT" {
return nil, fmt.Errorf("invalid decision value: %s", decision)
if decisionLine == "" {
return nil, fmt.Errorf("no decision found in response. Full response:\n%s", content)
}
explanation := string(bytes.TrimSpace(bytes.Join(lines[1:], []byte("\n"))))
// Clean and normalize decision
rawDecision := strings.TrimSpace(decisionLine)
// Remove "DECISION:" prefix if present and trim non-alphabetic characters
cleanDecision := strings.TrimPrefix(rawDecision, "DECISION:")
cleanDecision = strings.TrimFunc(cleanDecision, func(r rune) bool {
return !unicode.IsLetter(r) && !unicode.IsNumber(r)
})
// Normalize case and check for valid decision
upperDecision := strings.ToUpper(cleanDecision)
var decision string
switch {
case strings.HasPrefix(upperDecision, "ACCEPT"):
decision = "ACCEPT"
case strings.HasPrefix(upperDecision, "REJECT"):
decision = "REJECT"
default:
return nil, fmt.Errorf("invalid decision value: %q (cleaned: %q). Full response:\n%s",
rawDecision, cleanDecision, content)
}
// Get explanation as everything after the decision line
explanation := strings.TrimSpace(strings.Replace(content, decisionLine, "", 1))
return &decisionResult{
Decision: decision,