Skip to content

Commit f150e5b

Browse files
committed
feat(parser): table-fidelity loaders + XLSX/CSV; reframe pageindex citations; llmgate v0.3.0
Loaders: extract shared buildSections/deriveTitle (markdown/html/docx); DOCX & HTML tables serialize to Markdown grids; new XLSX (stdlib) + CSV loaders; DOCX outlineLvl heading fallback + footnotes; smarter plain-text heading splitting. Retrieval: reframe pageindex citation discipline from 'ideally ONE' to minimal-sufficient-set (widen 2+ carve-out, add two-range few-shot example). Deps: bump llmgate to v0.3.0 (GLM-4.6 pricing so per-query cost != 0; native tool calling; Usage.Priced). Wire pricing.WarnFunc in cmd/engine and cmd/server.
1 parent 1cbc94d commit f150e5b

14 files changed

Lines changed: 1114 additions & 206 deletions

File tree

cmd/engine/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"time"
2020

2121
"github.com/hallelx2/llmgate"
22+
"github.com/hallelx2/llmgate/pricing"
2223
"github.com/hallelx2/llmgate/provider/anthropic"
2324
"github.com/hallelx2/llmgate/provider/gemini"
2425
"github.com/hallelx2/llmgate/provider/openai"
@@ -61,6 +62,12 @@ func run() error {
6162
"retrieval_strategy", cfg.Retrieval.Strategy,
6263
)
6364

65+
// Surface any model with no price-book entry: its cost reads $0, which
66+
// would otherwise masquerade as "free" in usage/benchmark accounting.
67+
pricing.WarnFunc = func(model string) {
68+
logger.Warn("llm model not in price book; cost reported as 0", "model", model)
69+
}
70+
6471
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
6572
defer stop()
6673

cmd/server/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"time"
2828

2929
"github.com/hallelx2/llmgate"
30+
"github.com/hallelx2/llmgate/pricing"
3031
"github.com/hallelx2/llmgate/provider/anthropic"
3132
"github.com/hallelx2/llmgate/provider/gemini"
3233
"github.com/hallelx2/llmgate/provider/openai"
@@ -80,6 +81,12 @@ func run() error {
8081
"retrieval_strategy", cfg.Engine.Retrieval.Strategy,
8182
)
8283

84+
// Surface any model with no price-book entry: its cost reads $0, which
85+
// would otherwise masquerade as "free" in usage/benchmark accounting.
86+
pricing.WarnFunc = func(model string) {
87+
logger.Warn("llm model not in price book; cost reported as 0", "model", model)
88+
}
89+
8390
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
8491
defer stop()
8592

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ require (
1212
github.com/aws/smithy-go v1.25.0
1313
github.com/go-chi/chi/v5 v5.2.5
1414
github.com/google/uuid v1.6.0
15-
github.com/hallelx2/llmgate v0.2.0
15+
github.com/hallelx2/llmgate v0.3.0
1616
github.com/hallelx2/pdftable v0.3.1
1717
github.com/hibiken/asynq v0.26.0
1818
github.com/jackc/pgx/v5 v5.9.2

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,8 @@ github.com/googleapis/gax-go/v2 v2.21.0 h1:h45NjjzEO3faG9Lg/cFrBh2PgegVVgzqKzuZl
132132
github.com/googleapis/gax-go/v2 v2.21.0/go.mod h1:But/NJU6TnZsrLai/xBAQLLz+Hc7fHZJt/hsCz3Fih4=
133133
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww=
134134
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
135-
github.com/hallelx2/llmgate v0.2.0 h1:x/LNCeHUPZpafn2IXi+LqpnZa7TtEQdLVlpkkJTlzBI=
136-
github.com/hallelx2/llmgate v0.2.0/go.mod h1:MK2Ol/5CIweTQ2/9eSiTJ5g/KSSuobNZL9TD3s57JxY=
135+
github.com/hallelx2/llmgate v0.3.0 h1:OLm2L41WFAqE2ka64RUdLirXmVRbvbY8zu7N3NSkfS8=
136+
github.com/hallelx2/llmgate v0.3.0/go.mod h1:MK2Ol/5CIweTQ2/9eSiTJ5g/KSSuobNZL9TD3s57JxY=
137137
github.com/hallelx2/pdftable v0.3.1 h1:Uqe+9G8s9jrGYwxk8dEMXBCB+SlzvWPmW0Ze5863W1I=
138138
github.com/hallelx2/pdftable v0.3.1/go.mod h1:pxNlc4D43wjzis7M6EfgQZvHOsQ4okggm+xqUu+OokI=
139139
github.com/hhrutter/lzw v1.0.0 h1:laL89Llp86W3rRs83LvKbwYRx6INE8gDn0XNb1oXtm0=

pkg/ingest/ingest.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,6 +1116,8 @@ func DefaultRegistry() *parser.Registry {
11161116
parser.NewMarkdown(),
11171117
parser.NewHTML(),
11181118
parser.NewDOCX(),
1119+
parser.NewXLSX(),
1120+
parser.NewCSV(),
11191121
parser.NewPDF(),
11201122
parser.NewText(),
11211123
)
@@ -1150,6 +1152,8 @@ func RegistryFromIngestParams(opts *parser.TableOpts, maxSections int, parseTime
11501152
parser.NewMarkdown(),
11511153
parser.NewHTML(),
11521154
parser.NewDOCX(),
1155+
parser.NewXLSX(),
1156+
parser.NewCSV(),
11531157
parser.NewPDFWithConfig(opts, maxSections, parseTimeout),
11541158
parser.NewText(),
11551159
)

pkg/parser/csv.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package parser
2+
3+
import (
4+
"context"
5+
"encoding/csv"
6+
"io"
7+
"strings"
8+
)
9+
10+
// CSV parses comma-separated values into a single section rendered as a
11+
// Markdown grid, so the table structure (and each value's column) is
12+
// preserved for the LLM rather than collapsed into prose. The first row is
13+
// treated as the header.
14+
type CSV struct{}
15+
16+
// NewCSV returns a new CSV parser.
17+
func NewCSV() *CSV { return &CSV{} }
18+
19+
// Name implements Parser.
20+
func (*CSV) Name() string { return "csv" }
21+
22+
// Accepts implements Parser.
23+
func (*CSV) Accepts(contentType, filename string) bool {
24+
switch contentType {
25+
case "text/csv", "application/csv":
26+
return true
27+
}
28+
return HasExt(filename, ".csv")
29+
}
30+
31+
// Parse implements Parser.
32+
func (*CSV) Parse(_ context.Context, r io.Reader) (*ParsedDoc, error) {
33+
cr := csv.NewReader(r)
34+
cr.FieldsPerRecord = -1 // tolerate ragged rows
35+
cr.LazyQuotes = true
36+
37+
records, err := cr.ReadAll()
38+
if err != nil {
39+
return nil, err
40+
}
41+
42+
title := "Data"
43+
if len(records) > 0 && len(records[0]) > 0 {
44+
// Use the first header cell as a hint if it reads like a label.
45+
if h := strings.TrimSpace(records[0][0]); h != "" && len(h) <= 60 {
46+
title = h
47+
}
48+
}
49+
50+
content := renderTableMarkdown(records)
51+
return &ParsedDoc{
52+
Title: title,
53+
Sections: []Section{{
54+
Level: 1,
55+
Title: "Data",
56+
Content: content,
57+
}},
58+
}, nil
59+
}

0 commit comments

Comments
 (0)