chore(ts-migration): migrate ai engine to TypeScript
Phase 4b-i. Type-only, and the production bundle proves it: built from this
commit's parent and from this commit, all six chunk hashes match. The plain
emitted JavaScript is byte-identical at 34,095 bytes.
Four annotation sites, no logic touched:
- `ROLE_TITLES` and `AVAILABILITY_TOKENS` get `[RegExp, string][]`. Left to
inference the element widens to `string | RegExp`, which has no `.test`.
Explicit tuples rather than `as const`, which would also have worked and
would additionally have made the arrays readonly - a change to the type this
module publishes for no benefit it uses.
- `ROUTES` gets `[RegExp, (prompt: string) => any, number][]`, because all
three positions are used for what they are: `.test()` on the first, a call
on the second, `think(ms)` on the third.
- `invokeLLM` gets a real parameter type. It inferred `{ prompt?: string }`
from its own destructuring defaults, and that single inference was
responsible for nine errors in files this commit does not touch - eight in
`krowAi.js`, one in `provingGround.ts` - every one of them a caller passing
`response_json_schema` or `model`, which the real integration accepts.
Naming the options type fixes all nine from here.
- `uploadFile` gets `{ file?: File }`.
The return type of `invokeLLM` stays `Promise<any>`, deliberately. The ten
handlers behind the router return ten different shapes, and `krowAi.js` branches
on the result at run time - `typeof res === 'string' ? res : res.text || String(res)`
- which a precise union would reject on every branch without a `.text`. The
looseness is the contract, not an omission.
`InvokeLLMOptions` is exported as a type only; the runtime exports are still
exactly `invokeLLM` and `uploadFile`.
Verified in isolation from the parallel feature work (f96f128 plus this file):
tsc 53 -> 37, a set-difference against the baseline showing sixteen removed and
none added; skill-check 1641/1642 with only the known stale-fixture failure;
Owliver baseline 59/59; lint 0 errors; build succeeds with the API origin
inlined; the nine Owliver baseline artifacts unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HBG1wnuRfJKCstGB8Fekr8
This commit is contained in:
@@ -547,7 +547,11 @@ function matchTalent(prompt) {
|
||||
/* ── 7. Owliver profile builder ────────────────────────────────────────── */
|
||||
|
||||
/** Job titles a worker might name for themselves, most specific first. */
|
||||
const ROLE_TITLES = [
|
||||
/* Explicit tuple, not `as const`: the entries are read as a pair — `.find(([re]) => re.test(…))`
|
||||
then `role[1]` — and without it the element widens to `string | RegExp`, which has no `.test`.
|
||||
`as const` would also work and would additionally make the array and its entries `readonly`,
|
||||
a change to the type this module publishes for no benefit it uses. */
|
||||
const ROLE_TITLES: [RegExp, string][] = [
|
||||
[/banquet captain|captain/, 'Banquet Captain'],
|
||||
[/executive chef/, 'Executive Chef'],
|
||||
[/sous chef/, 'Sous Chef'],
|
||||
@@ -563,7 +567,8 @@ const ROLE_TITLES = [
|
||||
[/manager/, 'Manager'],
|
||||
];
|
||||
|
||||
const AVAILABILITY_TOKENS = [
|
||||
/** Same pairing, same reason — matched by regex, collected by label. */
|
||||
const AVAILABILITY_TOKENS: [RegExp, string][] = [
|
||||
[/weekend|saturday|sunday|sábado|domingo/i, 'Weekends'],
|
||||
[/weekday|monday|tuesday|wednesday|thursday|friday|semana/i, 'Weekdays'],
|
||||
[/evening|night|noche|tarde/i, 'Evenings'],
|
||||
@@ -768,7 +773,15 @@ function evaluateChallenge(prompt) {
|
||||
* Recognizes each workflow by the phrase its prompt opens with. Order matters
|
||||
* only in that every branch is mutually exclusive by design.
|
||||
*/
|
||||
const ROUTES = [
|
||||
/* `[pattern, handler, latencyMs]`. Spelled out because all three positions are used for what
|
||||
they are: `.test()` on the first, a call on the second, and `think(ms)` on the third. Left to
|
||||
inference the element becomes the union of all three and none of those work.
|
||||
|
||||
The handler returns `any` deliberately. The ten handlers below return ten different shapes,
|
||||
which is the point of a router — and `krowAi.js` relies on it: one caller does
|
||||
`typeof res === 'string' ? res : res.text || String(res)`, which a precise union would
|
||||
reject on every branch that has no `.text`. */
|
||||
const ROUTES: [RegExp, (prompt: string) => any, number][] = [
|
||||
[/^You are KROW's AI screening engine/m, screenCandidate, 1100],
|
||||
[/^You are an expert hiring copywriter/m, generateJobDescription, 1400],
|
||||
[/Extract and infer a structured resume/m, buildResume, 1200],
|
||||
@@ -782,8 +795,32 @@ const ROUTES = [
|
||||
() => 'Walk me through the exact order you would do that in — what happens first, and who do you tell?', 700],
|
||||
];
|
||||
|
||||
/** Drop-in replacement for `integrations.Core.InvokeLLM`. */
|
||||
export async function invokeLLM({ prompt = '', response_json_schema: schema } = {}) {
|
||||
/**
|
||||
* What a caller may send. Every field optional, because the destructuring default `= {}`
|
||||
* says a call with no argument at all is legal, and it is one this module answers.
|
||||
*
|
||||
* `model` and `file_urls` are not read here — the local engine routes on the prompt and
|
||||
* ignores both — but callers send them, and a parameter type that omitted them would reject
|
||||
* eight call sites in `krowAi.js` and one in `provingGround.ts` for passing a field the real
|
||||
* integration accepts. Declaring them describes the call this function is a drop-in for.
|
||||
*/
|
||||
export interface InvokeLLMOptions {
|
||||
prompt?: string;
|
||||
response_json_schema?: any;
|
||||
model?: string;
|
||||
file_urls?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop-in replacement for `integrations.Core.InvokeLLM`.
|
||||
*
|
||||
* The return stays `any` on purpose. See the note on `ROUTES`: the shape depends on which
|
||||
* handler matched, and narrowing it here would break callers that branch on the result at
|
||||
* run time rather than by type.
|
||||
*/
|
||||
export async function invokeLLM(
|
||||
{ prompt = '', response_json_schema: schema }: InvokeLLMOptions = {},
|
||||
): Promise<any> {
|
||||
const route = ROUTES.find(([pattern]) => pattern.test(prompt));
|
||||
|
||||
if (!route) {
|
||||
@@ -800,7 +837,7 @@ export async function invokeLLM({ prompt = '', response_json_schema: schema } =
|
||||
}
|
||||
|
||||
/** Drop-in replacement for `integrations.Core.UploadFile`. */
|
||||
export async function uploadFile({ file } = {}) {
|
||||
export async function uploadFile({ file }: { file?: File } = {}) {
|
||||
await think(500);
|
||||
if (!file) return { file_url: '' };
|
||||
// A blob URL keeps uploaded media viewable for the rest of the session
|
||||
Reference in New Issue
Block a user