๐Ÿ“ฆ EqualifyEverything / equalify-iris

๐Ÿ“„ context.ts ยท 72 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72import { readFileSync } from "node:fs";
import { extname } from "node:path";
import type { ProviderRouter, Image } from "../providers/index.ts";
import { IMAGE_MEDIA_TYPES } from "../providers/imageLimits.ts";
import type { Paths } from "../store/paths.ts";
import type { RunLog } from "../store/runlog.ts";
import type { IrisConfig } from "../config.ts";
import type { PdfLink } from "../util/pdf.ts";

export interface InputImage {
  name: string; // filename, e.g. page-001.png
  order: number; // 1-based processing order
  path: string;
  // The link annotations on this page, when it came from a PDF that had any โ€”
  // targets the image itself cannot carry (see pipeline/links.ts). Optional because
  // most pages have none, and because callers that synthesize an InputImage from
  // something other than an upload (the regression gate's fixture images) have no
  // links to give it.
  links?: PdfLink[];
}

// Everything a pipeline phase needs. Created once per run.
export interface PipelineContext {
  sessionId: string;
  cfg: IrisConfig;
  paths: Paths;
  router: ProviderRouter;
  log: RunLog;
  images: InputImage[];
  feedback?: string; // present on feedback re-runs
  maxReviewIterations: number;
  // Pages extracted in parallel in this run. Always a valid integer >= 1
  // (normalized by loadConfig), so consumers need no fallback.
  extractionConcurrency: number;
  // Corrected pages this run re-verifies for measurement only (config
  // `defaults.recheck_sample_size`; pipeline/correction.ts `recheckSampler`). Always a
  // valid integer >= 0, where 0 means the measurement is off. Carried here rather than
  // read off `cfg` at the claim site for the same reason as the line above: one place
  // resolves it, and a phase cannot disagree with the number the run was started with.
  recheckSampleSize: number;
  // The logged-in user's GitHub token โ€” used to file agent-suggestion issues
  // attributed to them (unless a service token override is configured).
  githubToken?: string;
}

// The extension -> media type map lives with the rest of the model's input limits
// (providers/imageLimits.ts), so the formats the upload route accepts and the media
// types sent to the model cannot disagree โ€” this file used to claim image/tiff for a
// format the model does not read.
export function mediaTypeFor(filename: string): string {
  return IMAGE_MEDIA_TYPES[extname(filename).toLowerCase()] ?? "image/png";
}

export function loadImage(img: InputImage): Image {
  return { data: readFileSync(img.path), media_type: mediaTypeFor(img.name) };
}

// Feedback is injected as a top-level instruction available to every
// downstream agent in the run. It is phrased as a required change so that, on an
// iterative feedback re-run, the Reader surfaces it as an issue (sourced to the
// affected block) and the Copy Editor applies it โ€” rather than the round no-opping
// when the document is otherwise accessibility-clean.
export function feedbackPreamble(ctx: PipelineContext): string {
  if (!ctx.feedback) return "";
  return (
    `\n\n## User feedback (top-level instruction โ€” applies to this whole run)\n` +
    `${ctx.feedback}\n\n` +
    `Treat this feedback as a REQUIRED change. Wherever it applies, flag the affected ` +
    `block(s) by their @source reference and make the change in your output.\n`
  );
}