@mrtdown/core 2.0.0-alpha.4 → 2.0.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/create.js +3 -3
- package/dist/cli/commands/create.js.map +1 -1
- package/dist/cli/commands/list.js +2 -2
- package/dist/cli/commands/list.js.map +1 -1
- package/dist/cli/commands/show.js +3 -3
- package/dist/cli/commands/show.js.map +1 -1
- package/dist/cli/commands/validate.js +1 -1
- package/dist/cli/commands/validate.js.map +1 -1
- package/dist/helpers/keyForAffectedEntity.d.ts +1 -1
- package/dist/helpers/keyForAffectedEntity.js.map +1 -1
- package/dist/helpers/normalizeRecurringPeriod.d.ts +1 -1
- package/dist/helpers/normalizeRecurringPeriod.js +1 -1
- package/dist/helpers/normalizeRecurringPeriod.js.map +1 -1
- package/dist/helpers/normalizeRecurringPeriod.test.js.map +1 -1
- package/dist/helpers/resolvePeriods.d.ts +1 -1
- package/dist/helpers/resolvePeriods.js +1 -1
- package/dist/helpers/resolvePeriods.js.map +1 -1
- package/dist/helpers/resolvePeriods.test.js.map +1 -1
- package/dist/index.d.ts +25 -25
- package/dist/index.js +25 -25
- package/dist/index.js.map +1 -1
- package/dist/llm/common/MemoryStore.d.ts +2 -2
- package/dist/llm/common/MemoryStore.js.map +1 -1
- package/dist/llm/common/formatCurrentState.d.ts +2 -2
- package/dist/llm/common/formatCurrentState.js.map +1 -1
- package/dist/llm/functions/extractClaimsFromNewEvidence/eval.test.js +3 -3
- package/dist/llm/functions/extractClaimsFromNewEvidence/eval.test.js.map +1 -1
- package/dist/llm/functions/extractClaimsFromNewEvidence/index.d.ts +2 -2
- package/dist/llm/functions/extractClaimsFromNewEvidence/index.js +4 -4
- package/dist/llm/functions/extractClaimsFromNewEvidence/index.js.map +1 -1
- package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindLinesTool.d.ts +1 -1
- package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindLinesTool.js.map +1 -1
- package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindServicesTool.d.ts +1 -1
- package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindServicesTool.js +1 -1
- package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindServicesTool.js.map +1 -1
- package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindStationsTool.d.ts +2 -2
- package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindStationsTool.js +2 -2
- package/dist/llm/functions/extractClaimsFromNewEvidence/tools/FindStationsTool.js.map +1 -1
- package/dist/llm/functions/generateIssueTitleAndSlug/index.js +2 -2
- package/dist/llm/functions/generateIssueTitleAndSlug/index.js.map +1 -1
- package/dist/llm/functions/translate/index.js +4 -4
- package/dist/llm/functions/translate/index.js.map +1 -1
- package/dist/llm/functions/triageNewEvidence/eval.test.js +2 -2
- package/dist/llm/functions/triageNewEvidence/eval.test.js.map +1 -1
- package/dist/llm/functions/triageNewEvidence/index.d.ts +1 -1
- package/dist/llm/functions/triageNewEvidence/index.js +4 -4
- package/dist/llm/functions/triageNewEvidence/index.js.map +1 -1
- package/dist/llm/functions/triageNewEvidence/tools/FindIssuesTool.d.ts +2 -2
- package/dist/llm/functions/triageNewEvidence/tools/FindIssuesTool.js +1 -1
- package/dist/llm/functions/triageNewEvidence/tools/FindIssuesTool.js.map +1 -1
- package/dist/llm/functions/triageNewEvidence/tools/GetIssueTool.d.ts +1 -1
- package/dist/llm/functions/triageNewEvidence/tools/GetIssueTool.js +1 -1
- package/dist/llm/functions/triageNewEvidence/tools/GetIssueTool.js.map +1 -1
- package/dist/repo/issue/IssueRepository.d.ts +1 -1
- package/dist/repo/issue/IssueRepository.js +3 -3
- package/dist/repo/issue/IssueRepository.js.map +1 -1
- package/dist/repo/issue/helpers/deriveCurrentState.d.ts +6 -6
- package/dist/repo/issue/helpers/deriveCurrentState.js +1 -1
- package/dist/repo/issue/helpers/deriveCurrentState.js.map +1 -1
- package/dist/repo/issue/helpers/deriveCurrentState.test.js.map +1 -1
- package/dist/schema/issue/evidence.js +1 -1
- package/dist/schema/issue/evidence.js.map +1 -1
- package/dist/schema/issue/issue.js +1 -1
- package/dist/schema/issue/issue.js.map +1 -1
- package/dist/util/ingestContent/helpers/getSlugDateTimeFromClaims.d.ts +1 -1
- package/dist/util/ingestContent/helpers/getSlugDateTimeFromClaims.js +1 -1
- package/dist/util/ingestContent/helpers/getSlugDateTimeFromClaims.js.map +1 -1
- package/dist/util/ingestContent/index.js +9 -9
- package/dist/util/ingestContent/index.js.map +1 -1
- package/dist/validators/buildContext.js +6 -6
- package/dist/validators/buildContext.js.map +1 -1
- package/dist/validators/issue.d.ts +1 -1
- package/dist/validators/issue.js +3 -3
- package/dist/validators/issue.js.map +1 -1
- package/dist/validators/line.d.ts +1 -1
- package/dist/validators/line.js +1 -1
- package/dist/validators/line.js.map +1 -1
- package/dist/validators/operator.js +1 -1
- package/dist/validators/operator.js.map +1 -1
- package/dist/validators/town.d.ts +1 -1
- package/dist/validators/town.js +1 -1
- package/dist/validators/town.js.map +1 -1
- package/dist/write/issue/IssueWriter.d.ts +3 -3
- package/dist/write/issue/IssueWriter.js.map +1 -1
- package/package.json +1 -10
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FindStationsTool.js","sourceRoot":"/","sources":["llm/functions/extractClaimsFromNewEvidence/tools/FindStationsTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjC,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,CAAC,MAAM,KAAK,CAAC;AACpB,OAAO,EAAE,IAAI,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"FindStationsTool.js","sourceRoot":"/","sources":["llm/functions/extractClaimsFromNewEvidence/tools/FindStationsTool.ts"],"names":[],"mappings":"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","sourcesContent":["import { DateTime } from 'luxon';\nimport type { Table } from 'mdast';\nimport { gfmToMarkdown } from 'mdast-util-gfm';\nimport { toMarkdown } from 'mdast-util-to-markdown';\nimport z from 'zod';\nimport { Tool } from '../../../common/tool.js';\nimport type { MRTDownRepository } from '../../../../repo/MRTDownRepository.js';\nimport { assert } from '../../../../util/assert.js';\n\nconst FindStationsToolParametersSchema = z.object({\n stationNames: z.array(z.string()),\n});\ntype FindStationsToolParameters = z.infer<\n typeof FindStationsToolParametersSchema\n>;\n\nexport class FindStationsTool extends Tool<FindStationsToolParameters> {\n public name = 'findStations';\n public description = 'Find stations by name';\n private readonly repo: MRTDownRepository;\n\n public get paramsSchema(): { [key: string]: unknown } {\n return z.toJSONSchema(FindStationsToolParametersSchema);\n }\n\n public parseParams(params: unknown): FindStationsToolParameters {\n return FindStationsToolParametersSchema.parse(params);\n }\n\n /**\n * The timestamp of the evidence.\n */\n private evidenceTs: DateTime;\n\n constructor(evidenceTs: DateTime, repo: MRTDownRepository) {\n super();\n this.evidenceTs = evidenceTs;\n this.repo = repo;\n }\n\n public async runner(params: FindStationsToolParameters): Promise<string> {\n console.log('[findStations] Calling tool with parameters:', params);\n\n const stations = this.repo.stations.searchByName(params.stationNames);\n\n const table: Table = {\n type: 'table',\n children: [\n {\n type: 'tableRow',\n children: [\n {\n type: 'tableCell',\n children: [{ type: 'text', value: 'Station ID' }],\n },\n {\n type: 'tableCell',\n children: [{ type: 'text', value: 'Station Name' }],\n },\n {\n type: 'tableCell',\n children: [{ type: 'text', value: 'Station Codes' }],\n },\n {\n type: 'tableCell',\n children: [{ type: 'text', value: 'Line IDs' }],\n },\n ],\n },\n ],\n };\n\n for (const station of stations) {\n const relevantStationCodes = station.stationCodes.filter((membership) => {\n const startedAt = DateTime.fromISO(membership.startedAt);\n assert(startedAt.isValid, `Invalid date: ${membership.startedAt}`);\n if (startedAt > this.evidenceTs) return false;\n if (\n membership.endedAt != null &&\n DateTime.fromISO(membership.endedAt) < this.evidenceTs\n )\n return false;\n return true;\n });\n\n const stationCodesSet = new Set<string>(\n relevantStationCodes.map((membership) => membership.code),\n );\n\n const lineIds = relevantStationCodes.map(\n (membership) => membership.lineId,\n );\n\n table.children.push({\n type: 'tableRow',\n children: [\n {\n type: 'tableCell',\n children: [{ type: 'text', value: station.id }],\n },\n {\n type: 'tableCell',\n children: [{ type: 'text', value: station.name['en-SG'] }],\n },\n {\n type: 'tableCell',\n children: [\n {\n type: 'text',\n value: Array.from(stationCodesSet).join(', '),\n },\n ],\n },\n {\n type: 'tableCell',\n children: [\n {\n type: 'text',\n value: lineIds.join(', '),\n },\n ],\n },\n ],\n });\n }\n\n const output = toMarkdown(table, {\n extensions: [gfmToMarkdown()],\n });\n console.log(`[findStations] Response output:\\n${output}`);\n\n return output;\n }\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { openAiClient } from '
|
|
3
|
-
import { assert } from '
|
|
2
|
+
import { openAiClient } from '../../client.js';
|
|
3
|
+
import { assert } from '../../../util/assert.js';
|
|
4
4
|
import { buildSystemPrompt } from './prompt.js';
|
|
5
5
|
const ResponseSchema = z.object({
|
|
6
6
|
title: z.string(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"/","sources":["llm/functions/generateIssueTitleAndSlug/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"/","sources":["llm/functions/generateIssueTitleAndSlug/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;IACjB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;CACjB,CAAC,CAAC;AAQH;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,MAAuC;IAEvC,MAAM,YAAY,GAAG,iBAAiB,EAAE,CAAC;IAEzC,MAAM,OAAO,GAAwB;QACnC;YACE,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE;QACP,MAAM,CAAC,IAAI;CAClB,CAAC,IAAI,EAAE;SACH;KACF,CAAC;IAEF,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC;QAClD,KAAK,EAAE,YAAY;QACnB,KAAK,EAAE,OAAO;QACd,YAAY,EAAE,YAAY;QAC1B,IAAI,EAAE;YACJ,MAAM,EAAE;gBACN,IAAI,EAAE,aAAa;gBACnB,IAAI,EAAE,UAAU;gBAChB,MAAM,EAAE,IAAI;gBACZ,MAAM,EAAE,CAAC,CAAC,YAAY,CAAC,cAAc,CAAC;aACvC;SACF;KACF,CAAC,CAAC;IAEH,MAAM,CAAC,QAAQ,CAAC,aAAa,IAAI,IAAI,EAAE,gCAAgC,CAAC,CAAC;IAEzE,OAAO,QAAQ,CAAC,aAAa,CAAC;AAChC,CAAC","sourcesContent":["import type { ResponseInputItem } from 'openai/resources/responses/responses.mjs';\nimport { z } from 'zod';\nimport { openAiClient } from '../../client.js';\nimport { assert } from '../../../util/assert.js';\nimport { buildSystemPrompt } from './prompt.js';\n\nconst ResponseSchema = z.object({\n title: z.string(),\n slug: z.string(),\n});\n\nexport type GenerateIssueTitleAndSlugParams = {\n text: string;\n};\n\nexport type GenerateIssueTitleAndSlugResult = z.infer<typeof ResponseSchema>;\n\n/**\n * Generate a title and slug for the given text.\n */\nexport async function generateIssueTitleAndSlug(\n params: GenerateIssueTitleAndSlugParams,\n): Promise<GenerateIssueTitleAndSlugResult> {\n const systemPrompt = buildSystemPrompt();\n\n const context: ResponseInputItem[] = [\n {\n role: 'user',\n content: `\nText: ${params.text}\n`.trim(),\n },\n ];\n\n const response = await openAiClient.responses.parse({\n model: 'gpt-5-nano',\n input: context,\n instructions: systemPrompt,\n text: {\n format: {\n type: 'json_schema',\n name: 'Response',\n strict: true,\n schema: z.toJSONSchema(ResponseSchema),\n },\n },\n });\n\n assert(response.output_parsed != null, 'Response output parsed is null');\n\n return response.output_parsed;\n}\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { estimateOpenAICostFromUsage, normalizeOpenAIResponsesUsage, } from '
|
|
3
|
-
import { openAiClient } from '
|
|
4
|
-
import { TranslationsSchema } from '
|
|
5
|
-
import { assert } from '
|
|
2
|
+
import { estimateOpenAICostFromUsage, normalizeOpenAIResponsesUsage, } from '../../../helpers/estimateOpenAICost.js';
|
|
3
|
+
import { openAiClient } from '../../client.js';
|
|
4
|
+
import { TranslationsSchema } from '../../../schema/common.js';
|
|
5
|
+
import { assert } from '../../../util/assert.js';
|
|
6
6
|
export async function translate(text) {
|
|
7
7
|
console.log('[translate] Translating text:', text);
|
|
8
8
|
const model = 'gpt-5-nano';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"/","sources":["llm/functions/translate/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,2BAA2B,EAC3B,6BAA6B,GAC9B,MAAM,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"/","sources":["llm/functions/translate/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,2BAA2B,EAC3B,6BAA6B,GAC9B,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEjD,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY;IAC1C,OAAO,CAAC,GAAG,CAAC,+BAA+B,EAAE,IAAI,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,YAAY,CAAC;IAE3B,MAAM,OAAO,GAAwB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAEvE,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC;QAClD,KAAK;QACL,KAAK,EAAE,OAAO;QACd,YAAY,EACV;;;;;;;;CAQL,CAAC,IAAI,EAAE;QACJ,IAAI,EAAE;YACJ,MAAM,EAAE;gBACN,IAAI,EAAE,aAAa;gBACnB,IAAI,EAAE,aAAa;gBACnB,MAAM,EAAE,IAAI;gBACZ,MAAM,EAAE,CAAC,CAAC,YAAY,CAAC,kBAAkB,CAAC;aAC3C;SACF;QACD,SAAS,EAAE;YACT,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,SAAS;SACnB;QACD,KAAK,EAAE,KAAK;QACZ,OAAO,EAAE,CAAC,6BAA6B,CAAC;KACzC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,6BAA6B,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC5D,MAAM,QAAQ,GAAG,2BAA2B,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/D,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAAE;YAChC,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;YAC1C,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,WAAW,EAAE,KAAK,CAAC,WAAW;SAC/B,CAAC,CAAC;QACH,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CACT,mCAAmC,EACnC,QAAQ,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,CACrC,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,gDAAgD,KAAK,IAAI,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;IAClD,CAAC;IAED,MAAM,CAAC,QAAQ,CAAC,aAAa,IAAI,IAAI,EAAE,gCAAgC,CAAC,CAAC;IAEzE,OAAO,QAAQ,CAAC,aAAa,CAAC;AAChC,CAAC","sourcesContent":["import type { ResponseInputItem } from 'openai/resources/responses/responses.js';\nimport { z } from 'zod';\nimport {\n estimateOpenAICostFromUsage,\n normalizeOpenAIResponsesUsage,\n} from '../../../helpers/estimateOpenAICost.js';\nimport { openAiClient } from '../../client.js';\nimport { TranslationsSchema } from '../../../schema/common.js';\nimport { assert } from '../../../util/assert.js';\n\nexport async function translate(text: string) {\n console.log('[translate] Translating text:', text);\n const model = 'gpt-5-nano';\n\n const context: ResponseInputItem[] = [{ role: 'user', content: text }];\n\n const response = await openAiClient.responses.parse({\n model,\n input: context,\n instructions:\n `You are a helpful assistant that translates text to the following languages:\n- English\n- Chinese (Simplified)\n- Malay\n- Tamil\n\nThese translations relate to transit disruption/maintenance/infrastructure issues and can contain names of lines and/or stations.\nKeep the names in English as much as possible, do not provide any translations for them.\n`.trim(),\n text: {\n format: {\n type: 'json_schema',\n name: 'Translation',\n strict: true,\n schema: z.toJSONSchema(TranslationsSchema),\n },\n },\n reasoning: {\n effort: 'minimal',\n summary: 'concise',\n },\n store: false,\n include: ['reasoning.encrypted_content'],\n });\n\n const usage = normalizeOpenAIResponsesUsage(response.usage);\n const estimate = estimateOpenAICostFromUsage({ model, usage });\n if (usage != null) {\n console.log('[translate] Usage:', {\n inputTokens: usage.inputTokens,\n cachedInputTokens: usage.cachedInputTokens,\n outputTokens: usage.outputTokens,\n totalTokens: usage.totalTokens,\n });\n if (estimate != null) {\n console.log(\n '[translate] Estimated cost (USD):',\n estimate.estimatedCostUsd.toFixed(8),\n );\n } else {\n console.log(`[translate] No pricing configured for model \"${model}\".`);\n }\n } else {\n console.log('[translate] Usage is unavailable');\n }\n\n assert(response.output_parsed != null, 'Response output parsed is null');\n\n return response.output_parsed;\n}\n"]}
|
|
@@ -2,8 +2,8 @@ import 'dotenv/config';
|
|
|
2
2
|
import { resolve } from 'node:path';
|
|
3
3
|
import { describe } from 'vitest';
|
|
4
4
|
import { describeEval, StructuredOutputScorer } from 'vitest-evals';
|
|
5
|
-
import { FileStore } from '
|
|
6
|
-
import { MRTDownRepository } from '
|
|
5
|
+
import { FileStore } from '../../../repo/common/FileStore.js';
|
|
6
|
+
import { MRTDownRepository } from '../../../repo/MRTDownRepository.js';
|
|
7
7
|
import { triageNewEvidence, } from './index.js';
|
|
8
8
|
describe('triageNewEvidence', () => {
|
|
9
9
|
describeEval('should triage the new evidence into an existing issue or a new issue', {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eval.test.js","sourceRoot":"/","sources":["llm/functions/triageNewEvidence/eval.test.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AAEvB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACpE,OAAO,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"eval.test.js","sourceRoot":"/","sources":["llm/functions/triageNewEvidence/eval.test.ts"],"names":[],"mappings":"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","sourcesContent":["import 'dotenv/config';\n\nimport { resolve } from 'node:path';\nimport { describe } from 'vitest';\nimport { describeEval, StructuredOutputScorer } from 'vitest-evals';\nimport { FileStore } from '../../../repo/common/FileStore.js';\nimport { MRTDownRepository } from '../../../repo/MRTDownRepository.js';\nimport {\n type TriageNewEvidenceParams,\n type TriageNewEvidenceResult,\n triageNewEvidence,\n} from './index.js';\n\ndescribe('triageNewEvidence', () => {\n describeEval(\n 'should triage the new evidence into an existing issue or a new issue',\n {\n // @ts-expect-error input is a string in the vitest-evals library\n async data() {\n const store = new FileStore(\n resolve(import.meta.dirname, '../../fixtures/data'),\n );\n const repo = new MRTDownRepository({ store });\n\n return [\n {\n input: {\n newEvidence: {\n ts: '2026-01-01T07:10:00+08:00',\n text: '[TGL] Due to a track fault at Tengah, train services on the Tengah Line are delayed between Bukit Batok and Bukit Merah Central',\n },\n repo,\n // This is used by vitest-evals as the test name, as the library expects `input` to be a string.\n toString() {\n return '[DISRUPTION] Related to an existing issue';\n },\n },\n expected: {\n result: {\n kind: 'part-of-existing-issue',\n issueId: '2026-01-01-tgl-train-fault',\n },\n },\n },\n {\n input: {\n newEvidence: {\n ts: '2026-01-01T07:10:00+08:00',\n text: '[TGL] Due to a track fault at Tengah, train services on the Tengah Line are delayed between Bukit Merah Central and Outram Park',\n },\n repo,\n // This is used by vitest-evals as the test name, as the library expects `input` to be a string.\n toString() {\n return '[DISRUPTION] Related to a new issue on a different part of the same line';\n },\n },\n expected: {\n result: {\n kind: 'part-of-new-issue',\n issueType: 'disruption',\n },\n },\n },\n {\n input: {\n newEvidence: {\n ts: '2026-01-01T07:10:00+08:00',\n text: '[TGL] Due to maintenance works, services on the Tengah Line will end earlier at 11pm tonight.',\n },\n repo,\n // This is used by vitest-evals as the test name, as the library expects `input` to be a string.\n toString() {\n return '[MAINTENANCE] Related to a new issue';\n },\n },\n expected: {\n result: {\n kind: 'part-of-new-issue',\n issueType: 'maintenance',\n },\n },\n },\n {\n input: {\n newEvidence: {\n ts: '2026-03-01T07:10:00+08:00',\n text: '[SLL] Due to a track fault at Seletar, train services on the Seletar Line are delayed between Seletar Aerospace and Bukit Merah Central',\n },\n repo: new MRTDownRepository({\n store: new FileStore(\n resolve(import.meta.dirname, '../fixtures/data'),\n ),\n }),\n // This is used by vitest-evals as the test name, as the library expects `input` to be a string.\n toString() {\n return '[DISRUPTION] Related to a new issue';\n },\n },\n expected: {\n result: {\n kind: 'part-of-new-issue',\n issueType: 'disruption',\n },\n },\n },\n {\n input: {\n newEvidence: {\n ts: '2026-03-01T07:10:00+08:00',\n text: '[SLL] MRT Platform screen doors at Seletar Line stations will undergo renewal works from 1st March to 31st March 2026.',\n },\n repo,\n // This is used by vitest-evals as the test name, as the library expects `input` to be a string.\n toString() {\n return '[INFRA] Related to a new issue';\n },\n },\n expected: {\n result: {\n kind: 'part-of-new-issue',\n issueType: 'infra',\n },\n },\n },\n {\n input: {\n newEvidence: {\n ts: '2026-03-01T07:10:00+08:00',\n text: \"Singapore's MRT system is the best in the world.\",\n },\n repo,\n // This is used by vitest-evals as the test name, as the library expects `input` to be a string.\n toString() {\n return 'Irrelevant content';\n },\n },\n expected: {\n result: {\n kind: 'irrelevant-content',\n },\n },\n },\n ] satisfies {\n input: TriageNewEvidenceParams & { toString(): string };\n expected: TriageNewEvidenceResult;\n }[];\n },\n async task(input) {\n const result = await triageNewEvidence(\n input as unknown as TriageNewEvidenceParams,\n );\n return JSON.stringify(result);\n },\n scorers: [StructuredOutputScorer()],\n },\n );\n});\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import z from 'zod';
|
|
2
|
-
import type { MRTDownRepository } from '
|
|
2
|
+
import type { MRTDownRepository } from '../../../repo/MRTDownRepository.js';
|
|
3
3
|
declare const ResponseSchema: z.ZodObject<{
|
|
4
4
|
result: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
5
5
|
kind: z.ZodLiteral<"part-of-existing-issue">;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { DateTime } from 'luxon';
|
|
2
2
|
import z from 'zod';
|
|
3
|
-
import { openAiClient } from '
|
|
4
|
-
import { IssueIdSchema } from '
|
|
5
|
-
import { IssueTypeSchema } from '
|
|
6
|
-
import { assert } from '
|
|
3
|
+
import { openAiClient } from '../../client.js';
|
|
4
|
+
import { IssueIdSchema } from '../../../schema/issue/id.js';
|
|
5
|
+
import { IssueTypeSchema } from '../../../schema/issue/issueType.js';
|
|
6
|
+
import { assert } from '../../../util/assert.js';
|
|
7
7
|
import { buildSystemPrompt } from './prompt.js';
|
|
8
8
|
import { FindIssuesTool } from './tools/FindIssuesTool.js';
|
|
9
9
|
import { GetIssueTool } from './tools/GetIssueTool.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"/","sources":["llm/functions/triageNewEvidence/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAKjC,OAAO,CAAC,MAAM,KAAK,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"/","sources":["llm/functions/triageNewEvidence/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { DateTime } from 'luxon';\nimport type {\n ParsedResponse,\n ResponseInputItem,\n} from 'openai/resources/responses/responses.mjs';\nimport z from 'zod';\nimport { openAiClient } from '../../client.js';\nimport type { MRTDownRepository } from '../../../repo/MRTDownRepository.js';\nimport { IssueIdSchema } from '../../../schema/issue/id.js';\nimport { IssueTypeSchema } from '../../../schema/issue/issueType.js';\nimport { assert } from '../../../util/assert.js';\nimport type { ToolRegistry } from '../../common/tool.js';\nimport { buildSystemPrompt } from './prompt.js';\nimport { FindIssuesTool } from './tools/FindIssuesTool.js';\nimport { GetIssueTool } from './tools/GetIssueTool.js';\n\nconst TOOL_CALL_LIMIT = 5;\n\nconst ResponseSchema = z.object({\n result: z.discriminatedUnion('type', [\n z.object({\n kind: z.literal('part-of-existing-issue'),\n issueId: IssueIdSchema,\n }),\n z.object({\n kind: z.literal('part-of-new-issue'),\n issueType: IssueTypeSchema,\n }),\n z.object({\n kind: z.literal('irrelevant-content'),\n }),\n ]),\n});\n\nexport type TriageNewEvidenceParams = {\n newEvidence: {\n ts: string;\n text: string;\n };\n repo: MRTDownRepository;\n};\n\nexport type TriageNewEvidenceResult = z.infer<typeof ResponseSchema>;\n\nexport async function triageNewEvidence(params: TriageNewEvidenceParams) {\n const evidenceTs = DateTime.fromISO(params.newEvidence.ts);\n assert(evidenceTs.isValid, `Invalid date: ${params.newEvidence.ts}`);\n\n const findIssuesTool = new FindIssuesTool(params.repo);\n const getIssueTool = new GetIssueTool(params.repo);\n const toolRegistry: ToolRegistry = {\n [findIssuesTool.name]: findIssuesTool,\n [getIssueTool.name]: getIssueTool,\n };\n\n const systemPrompt = buildSystemPrompt();\n\n const context: ResponseInputItem[] = [\n {\n role: 'user',\n content: `\nEvidence: ${params.newEvidence.text}\n\nTimestamp: ${evidenceTs.toISO({ includeOffset: true })}\n`.trim(),\n },\n ];\n\n let toolCallCount = 0;\n\n let response: ParsedResponse<z.infer<typeof ResponseSchema>>;\n do {\n response = await openAiClient.responses.parse({\n model: 'gpt-5-mini',\n input: context,\n instructions: systemPrompt,\n text: {\n format: {\n type: 'json_schema',\n name: 'Response',\n strict: true,\n schema: z.toJSONSchema(ResponseSchema),\n },\n },\n tools: Object.values(toolRegistry).map((tool) => {\n return {\n type: 'function',\n name: tool.name,\n description: tool.description,\n parameters: tool.paramsSchema,\n strict: true,\n };\n }),\n // Don't persist conversation with OpenAI, but include reasoning content to\n // continue the thread with the same reasoning.\n store: false,\n include: ['reasoning.encrypted_content'],\n });\n\n for (const item of response.output) {\n switch (item.type) {\n case 'function_call': {\n /**\n * Prevent the `parsed_arguments` field from being included\n * https://github.com/openai/openai-python/issues/2374\n */\n context.push({\n type: 'function_call',\n id: item.id,\n call_id: item.call_id,\n name: item.name,\n arguments: item.arguments,\n });\n\n if (toolCallCount > TOOL_CALL_LIMIT) {\n context.push({\n type: 'function_call_output',\n call_id: item.call_id,\n output: 'Ran out of tool calls. Stop Calling.',\n });\n console.log(\n 'Forced short-circuit, returning error message in tool call result.',\n );\n }\n\n if (item.name in toolRegistry) {\n const tool = toolRegistry[item.name];\n\n const params = tool.parseParams(JSON.parse(item.arguments));\n // Call the tool's run function\n const result = await tool.runner(params);\n\n context.push({\n type: 'function_call_output',\n call_id: item.call_id,\n output: result,\n });\n }\n\n toolCallCount++;\n break;\n }\n default: {\n context.push(item);\n break;\n }\n }\n }\n } while (response.output.some((item) => item.type === 'function_call'));\n\n assert(response.output_parsed != null, 'Response output parsed is null');\n\n return response.output_parsed;\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import z from 'zod';
|
|
2
|
-
import { Tool } from '
|
|
3
|
-
import type { MRTDownRepository } from '
|
|
2
|
+
import { Tool } from '../../../common/tool.js';
|
|
3
|
+
import type { MRTDownRepository } from '../../../../repo/MRTDownRepository.js';
|
|
4
4
|
declare const FindIssuesToolParametersSchema: z.ZodObject<{
|
|
5
5
|
query: z.ZodString;
|
|
6
6
|
}, z.z.core.$strip>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { gfmToMarkdown } from 'mdast-util-gfm';
|
|
2
2
|
import { toMarkdown } from 'mdast-util-to-markdown';
|
|
3
3
|
import z from 'zod';
|
|
4
|
-
import { Tool } from '
|
|
4
|
+
import { Tool } from '../../../common/tool.js';
|
|
5
5
|
const FindIssuesToolParametersSchema = z.object({
|
|
6
6
|
query: z.string().describe('Plain text to search for issues.'),
|
|
7
7
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FindIssuesTool.js","sourceRoot":"/","sources":["llm/functions/triageNewEvidence/tools/FindIssuesTool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,CAAC,MAAM,KAAK,CAAC;AACpB,OAAO,EAAE,IAAI,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"FindIssuesTool.js","sourceRoot":"/","sources":["llm/functions/triageNewEvidence/tools/FindIssuesTool.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,CAAC,MAAM,KAAK,CAAC;AACpB,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAG/C,MAAM,8BAA8B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;CAC/D,CAAC,CAAC;AAGH,MAAM,OAAO,cAAe,SAAQ,IAA8B;IACzD,IAAI,GAAG,YAAY,CAAC;IACpB,WAAW,GAAG,sBAAsB,CAAC;IAC3B,IAAI,CAAoB;IAEzC,YAAY,IAAuB;QACjC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,IAAW,YAAY;QACrB,OAAO,CAAC,CAAC,YAAY,CAAC,8BAA8B,CAAC,CAAC;IACxD,CAAC;IAEM,WAAW,CAAC,MAAe;QAChC,OAAO,8BAA8B,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACtD,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,MAAgC;QAClD,OAAO,CAAC,GAAG,CAAC,4CAA4C,EAAE,MAAM,CAAC,CAAC;QAElE,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAE5D,MAAM,UAAU,GAAU;YACxB,IAAI,EAAE,OAAO;YACb,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,UAAU;oBAChB,QAAQ,EAAE;wBACR;4BACE,IAAI,EAAE,WAAW;4BACjB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;yBAChD;wBACD;4BACE,IAAI,EAAE,WAAW;4BACjB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC;yBACnD;qBACF;iBACF;aACF;SACF,CAAC;QAEF,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC;gBACvB,IAAI,EAAE,UAAU;gBAChB,QAAQ,EAAE;oBACR;wBACE,IAAI,EAAE,WAAW;wBACjB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;qBACpD;oBACD;wBACE,IAAI,EAAE,WAAW;wBACjB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;qBAChE;iBACF;aACF,CAAC,CAAC;QACL,CAAC;QAED,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,EAAE;YACpC,UAAU,EAAE,CAAC,aAAa,EAAE,CAAC;SAC9B,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,kCAAkC,MAAM,EAAE,CAAC,CAAC;QAExD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF","sourcesContent":["import type { Table } from 'mdast';\nimport { gfmToMarkdown } from 'mdast-util-gfm';\nimport { toMarkdown } from 'mdast-util-to-markdown';\nimport z from 'zod';\nimport { Tool } from '../../../common/tool.js';\nimport type { MRTDownRepository } from '../../../../repo/MRTDownRepository.js';\n\nconst FindIssuesToolParametersSchema = z.object({\n query: z.string().describe('Plain text to search for issues.'),\n});\ntype FindIssuesToolParameters = z.infer<typeof FindIssuesToolParametersSchema>;\n\nexport class FindIssuesTool extends Tool<FindIssuesToolParameters> {\n public name = 'findIssues';\n public description = 'Find issues by query';\n private readonly repo: MRTDownRepository;\n\n constructor(repo: MRTDownRepository) {\n super();\n this.repo = repo;\n }\n\n public get paramsSchema(): { [key: string]: unknown } {\n return z.toJSONSchema(FindIssuesToolParametersSchema);\n }\n\n public parseParams(params: unknown): FindIssuesToolParameters {\n return FindIssuesToolParametersSchema.parse(params);\n }\n\n public async runner(params: FindIssuesToolParameters): Promise<string> {\n console.log('[findIssues] Calling tool with parameters:', params);\n\n const issues = this.repo.issues.searchByQuery(params.query);\n\n const issueTable: Table = {\n type: 'table',\n children: [\n {\n type: 'tableRow',\n children: [\n {\n type: 'tableCell',\n children: [{ type: 'text', value: 'Issue ID' }],\n },\n {\n type: 'tableCell',\n children: [{ type: 'text', value: 'Issue Title' }],\n },\n ],\n },\n ],\n };\n\n for (const issue of issues) {\n issueTable.children.push({\n type: 'tableRow',\n children: [\n {\n type: 'tableCell',\n children: [{ type: 'text', value: issue.issue.id }],\n },\n {\n type: 'tableCell',\n children: [{ type: 'text', value: issue.issue.title['en-SG'] }],\n },\n ],\n });\n }\n\n const output = toMarkdown(issueTable, {\n extensions: [gfmToMarkdown()],\n });\n console.log(`[findIssues] Response output:\\n${output}`);\n\n return output;\n }\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import z from 'zod';
|
|
2
|
-
import type { MRTDownRepository } from '
|
|
2
|
+
import type { MRTDownRepository } from '../../../../repo/MRTDownRepository.js';
|
|
3
3
|
import { Tool } from '../../../common/tool.js';
|
|
4
4
|
declare const GetIssueToolParametersSchema: z.ZodObject<{
|
|
5
5
|
issueId: z.ZodString;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import z from 'zod';
|
|
2
|
-
import { deriveCurrentState } from '
|
|
2
|
+
import { deriveCurrentState } from '../../../../repo/issue/helpers/deriveCurrentState.js';
|
|
3
3
|
import { formatCurrentState } from '../../../common/formatCurrentState.js';
|
|
4
4
|
import { Tool } from '../../../common/tool.js';
|
|
5
5
|
const GetIssueToolParametersSchema = z.object({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GetIssueTool.js","sourceRoot":"/","sources":["llm/functions/triageNewEvidence/tools/GetIssueTool.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,KAAK,CAAC;AACpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"GetIssueTool.js","sourceRoot":"/","sources":["llm/functions/triageNewEvidence/tools/GetIssueTool.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,KAAK,CAAC;AACpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,sDAAsD,CAAC;AAE1F,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAC3E,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAE/C,MAAM,4BAA4B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;CACpB,CAAC,CAAC;AAGH,MAAM,OAAO,YAAa,SAAQ,IAA4B;IACrD,IAAI,GAAG,UAAU,CAAC;IAClB,WAAW,GAAG,oBAAoB,CAAC;IACzB,IAAI,CAAoB;IAEzC,YAAY,IAAuB;QACjC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,IAAW,YAAY;QACrB,OAAO,CAAC,CAAC,YAAY,CAAC,4BAA4B,CAAC,CAAC;IACtD,CAAC;IAEM,WAAW,CAAC,MAAe;QAChC,OAAO,4BAA4B,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACpD,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,MAA8B;QAChD,OAAO,CAAC,GAAG,CAAC,0CAA0C,EAAE,MAAM,CAAC,CAAC;QAEhE,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACzD,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;YACxB,OAAO,SAAS,MAAM,CAAC,OAAO,YAAY,CAAC;QAC7C,CAAC;QAED,MAAM,YAAY,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC;QAErD,MAAM,MAAM,GAAG,kBAAkB,CAAC;YAChC,KAAK,EAAE,YAAY;YACnB,QAAQ,EAAE,WAAW,CAAC,QAAQ;SAC/B,CAAC,CAAC;QACH,OAAO,CAAC,GAAG,CAAC,gCAAgC,MAAM,EAAE,CAAC,CAAC;QAEtD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF","sourcesContent":["import z from 'zod';\nimport { deriveCurrentState } from '../../../../repo/issue/helpers/deriveCurrentState.js';\nimport type { MRTDownRepository } from '../../../../repo/MRTDownRepository.js';\nimport { formatCurrentState } from '../../../common/formatCurrentState.js';\nimport { Tool } from '../../../common/tool.js';\n\nconst GetIssueToolParametersSchema = z.object({\n issueId: z.string(),\n});\ntype GetIssueToolParameters = z.infer<typeof GetIssueToolParametersSchema>;\n\nexport class GetIssueTool extends Tool<GetIssueToolParameters> {\n public name = 'getIssue';\n public description = 'Get an issue by ID';\n private readonly repo: MRTDownRepository;\n\n constructor(repo: MRTDownRepository) {\n super();\n this.repo = repo;\n }\n\n public get paramsSchema(): { [key: string]: unknown } {\n return z.toJSONSchema(GetIssueToolParametersSchema);\n }\n\n public parseParams(params: unknown): GetIssueToolParameters {\n return GetIssueToolParametersSchema.parse(params);\n }\n\n public async runner(params: GetIssueToolParameters): Promise<string> {\n console.log('[getIssue] Calling tool with parameters:', params);\n\n const issueBundle = this.repo.issues.get(params.issueId);\n if (issueBundle == null) {\n return `Issue ${params.issueId} not found`;\n }\n\n const currentState = deriveCurrentState(issueBundle);\n\n const output = formatCurrentState({\n state: currentState,\n evidence: issueBundle.evidence,\n });\n console.log(`[getIssue] Response output:\\n${output}`);\n\n return output;\n }\n}\n"]}
|
|
@@ -2,9 +2,9 @@ import { join } from 'node:path';
|
|
|
2
2
|
import Fuse from 'fuse.js';
|
|
3
3
|
import { NdJson } from 'json-nd';
|
|
4
4
|
import z from 'zod';
|
|
5
|
-
import { EvidenceSchema } from '
|
|
6
|
-
import { ImpactEventSchema } from '
|
|
7
|
-
import { IssueSchema } from '
|
|
5
|
+
import { EvidenceSchema } from '../../schema/issue/evidence.js';
|
|
6
|
+
import { ImpactEventSchema } from '../../schema/issue/impactEvent.js';
|
|
7
|
+
import { IssueSchema } from '../../schema/issue/issue.js';
|
|
8
8
|
import { DIR_ISSUE } from '../../constants.js';
|
|
9
9
|
export class IssueRepository {
|
|
10
10
|
store;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IssueRepository.js","sourceRoot":"/","sources":["repo/issue/IssueRepository.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from 'node:path';\nimport Fuse, { type Expression } from 'fuse.js';\nimport { NdJson } from 'json-nd';\nimport z from 'zod';\nimport type { IssueBundle } from '#schema/issue/bundle.js';\nimport { EvidenceSchema } from '#schema/issue/evidence.js';\nimport { ImpactEventSchema } from '#schema/issue/impactEvent.js';\nimport { IssueSchema } from '#schema/issue/issue.js';\nimport { DIR_ISSUE } from '../../constants.js';\nimport type { IStore } from '../common/store.js';\n\ntype IssueIndexEntry = {\n issueId: string;\n path: string; // e.g. data/issues/2026/01/01/<issueId>\n year: number;\n month: number;\n};\n\nexport class IssueRepository {\n private readonly store: IStore;\n\n constructor(store: IStore) {\n this.store = store;\n }\n\n // Index for fast lookup by issue ID.\n private byId = new Map<string, IssueIndexEntry>();\n private indexed = false;\n\n // Cache for issue bundles.\n private bundleCache = new Map<string, IssueBundle>();\n\n /**\n * Build the index of all issues.\n */\n private buildIndex() {\n if (this.indexed) {\n return;\n }\n\n const years = this.store.listDir(DIR_ISSUE);\n for (const year of years) {\n if (!/^\\d{4}$/.test(year)) {\n continue;\n }\n const months = this.store.listDir(join(DIR_ISSUE, year));\n for (const month of months) {\n if (!/^\\d{2}$/.test(month)) {\n continue;\n }\n const issues = this.store.listDir(join(DIR_ISSUE, year, month));\n for (const issueId of issues) {\n this.byId.set(issueId, {\n issueId: issueId,\n path: join(DIR_ISSUE, year, month, issueId),\n year: Number(year),\n month: Number(month),\n });\n }\n }\n }\n this.indexed = true;\n }\n\n /**\n * Return the relative folder path to the issue.\n * @param id\n * @returns\n */\n getPath(id: string): string | null {\n this.buildIndex();\n return this.byId.get(id)?.path ?? null;\n }\n\n get(id: string): IssueBundle | null {\n this.buildIndex();\n\n const isCached = this.bundleCache.has(id);\n if (isCached) {\n return this.bundleCache.get(id) ?? null;\n }\n\n const entry = this.byId.get(id);\n if (!entry) {\n return null;\n }\n\n const issue = IssueSchema.parse(\n this.store.readJson(join(entry.path, 'issue.json')),\n );\n\n const evidencePath = join(entry.path, 'evidence.ndjson');\n const evidenceRaw = this.store.exists(evidencePath)\n ? this.store.readText(evidencePath).trim()\n : '';\n const evidence = z\n .array(EvidenceSchema)\n .parse(evidenceRaw ? NdJson.parse(evidenceRaw) : []);\n\n const impactPath = join(entry.path, 'impact.ndjson');\n const impactRaw = this.store.exists(impactPath)\n ? this.store.readText(impactPath).trim()\n : '';\n const impact = z\n .array(ImpactEventSchema)\n .parse(impactRaw ? NdJson.parse(impactRaw) : []);\n\n const bundle: IssueBundle = {\n issue,\n evidence,\n impactEvents: impact,\n path: entry.path,\n };\n this.bundleCache.set(id, bundle);\n return bundle;\n }\n\n /**\n * List all issue IDs, sorted by year and month.\n * @returns\n */\n listIds(): string[] {\n this.buildIndex();\n const entries = Array.from(this.byId.values());\n entries.sort((a, b) => {\n if (a.year !== b.year) {\n return a.year - b.year;\n }\n if (a.month !== b.month) {\n return a.month - b.month;\n }\n return a.issueId.localeCompare(b.issueId);\n });\n return entries.map((entry) => entry.issueId);\n }\n\n /**\n * List all bundles, sorted by year and month.\n * @returns\n */\n list(): IssueBundle[] {\n this.buildIndex();\n const entries = Array.from(this.byId.values());\n entries.sort((a, b) => {\n if (a.year !== b.year) {\n return a.year - b.year;\n }\n if (a.month !== b.month) {\n return a.month - b.month;\n }\n return a.issueId.localeCompare(b.issueId);\n });\n const result: IssueBundle[] = [];\n for (const entry of entries) {\n const bundle = this.get(entry.issueId);\n if (bundle == null) {\n continue;\n }\n result.push(bundle);\n }\n return result;\n }\n\n /**\n * Search issues by query.\n * @param query\n * @returns\n */\n searchByQuery(query: string): IssueBundle[] {\n const fuse = new Fuse(this.list(), {\n keys: [\n 'issue.id',\n 'issue.title.en-SG',\n 'evidence.text',\n 'impact.scopeItems.serviceId',\n 'impact.scopeItems.stationId',\n 'impact.scopeItems.fromStationId',\n 'impact.scopeItems.toStationId',\n ],\n includeScore: true,\n threshold: 0.8,\n });\n const searchResults = fuse.search({\n $or: [\n { 'issue.id': query },\n { 'issue.title.en-SG': query },\n { 'evidence.text': query },\n { 'impact.scopeItems.serviceId': query },\n { 'impact.scopeItems.stationId': query },\n { 'impact.scopeItems.fromStationId': query },\n { 'impact.scopeItems.toStationId': query },\n ] as Expression[],\n });\n const results: IssueBundle[] = [];\n for (const result of searchResults) {\n const bundle = this.get(result.item.issue.id);\n if (bundle == null) {\n continue;\n }\n results.push(bundle);\n }\n return results;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"IssueRepository.js","sourceRoot":"/","sources":["repo/issue/IssueRepository.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from 'node:path';\nimport Fuse, { type Expression } from 'fuse.js';\nimport { NdJson } from 'json-nd';\nimport z from 'zod';\nimport type { IssueBundle } from '../../schema/issue/bundle.js';\nimport { EvidenceSchema } from '../../schema/issue/evidence.js';\nimport { ImpactEventSchema } from '../../schema/issue/impactEvent.js';\nimport { IssueSchema } from '../../schema/issue/issue.js';\nimport { DIR_ISSUE } from '../../constants.js';\nimport type { IStore } from '../common/store.js';\n\ntype IssueIndexEntry = {\n issueId: string;\n path: string; // e.g. data/issues/2026/01/01/<issueId>\n year: number;\n month: number;\n};\n\nexport class IssueRepository {\n private readonly store: IStore;\n\n constructor(store: IStore) {\n this.store = store;\n }\n\n // Index for fast lookup by issue ID.\n private byId = new Map<string, IssueIndexEntry>();\n private indexed = false;\n\n // Cache for issue bundles.\n private bundleCache = new Map<string, IssueBundle>();\n\n /**\n * Build the index of all issues.\n */\n private buildIndex() {\n if (this.indexed) {\n return;\n }\n\n const years = this.store.listDir(DIR_ISSUE);\n for (const year of years) {\n if (!/^\\d{4}$/.test(year)) {\n continue;\n }\n const months = this.store.listDir(join(DIR_ISSUE, year));\n for (const month of months) {\n if (!/^\\d{2}$/.test(month)) {\n continue;\n }\n const issues = this.store.listDir(join(DIR_ISSUE, year, month));\n for (const issueId of issues) {\n this.byId.set(issueId, {\n issueId: issueId,\n path: join(DIR_ISSUE, year, month, issueId),\n year: Number(year),\n month: Number(month),\n });\n }\n }\n }\n this.indexed = true;\n }\n\n /**\n * Return the relative folder path to the issue.\n * @param id\n * @returns\n */\n getPath(id: string): string | null {\n this.buildIndex();\n return this.byId.get(id)?.path ?? null;\n }\n\n get(id: string): IssueBundle | null {\n this.buildIndex();\n\n const isCached = this.bundleCache.has(id);\n if (isCached) {\n return this.bundleCache.get(id) ?? null;\n }\n\n const entry = this.byId.get(id);\n if (!entry) {\n return null;\n }\n\n const issue = IssueSchema.parse(\n this.store.readJson(join(entry.path, 'issue.json')),\n );\n\n const evidencePath = join(entry.path, 'evidence.ndjson');\n const evidenceRaw = this.store.exists(evidencePath)\n ? this.store.readText(evidencePath).trim()\n : '';\n const evidence = z\n .array(EvidenceSchema)\n .parse(evidenceRaw ? NdJson.parse(evidenceRaw) : []);\n\n const impactPath = join(entry.path, 'impact.ndjson');\n const impactRaw = this.store.exists(impactPath)\n ? this.store.readText(impactPath).trim()\n : '';\n const impact = z\n .array(ImpactEventSchema)\n .parse(impactRaw ? NdJson.parse(impactRaw) : []);\n\n const bundle: IssueBundle = {\n issue,\n evidence,\n impactEvents: impact,\n path: entry.path,\n };\n this.bundleCache.set(id, bundle);\n return bundle;\n }\n\n /**\n * List all issue IDs, sorted by year and month.\n * @returns\n */\n listIds(): string[] {\n this.buildIndex();\n const entries = Array.from(this.byId.values());\n entries.sort((a, b) => {\n if (a.year !== b.year) {\n return a.year - b.year;\n }\n if (a.month !== b.month) {\n return a.month - b.month;\n }\n return a.issueId.localeCompare(b.issueId);\n });\n return entries.map((entry) => entry.issueId);\n }\n\n /**\n * List all bundles, sorted by year and month.\n * @returns\n */\n list(): IssueBundle[] {\n this.buildIndex();\n const entries = Array.from(this.byId.values());\n entries.sort((a, b) => {\n if (a.year !== b.year) {\n return a.year - b.year;\n }\n if (a.month !== b.month) {\n return a.month - b.month;\n }\n return a.issueId.localeCompare(b.issueId);\n });\n const result: IssueBundle[] = [];\n for (const entry of entries) {\n const bundle = this.get(entry.issueId);\n if (bundle == null) {\n continue;\n }\n result.push(bundle);\n }\n return result;\n }\n\n /**\n * Search issues by query.\n * @param query\n * @returns\n */\n searchByQuery(query: string): IssueBundle[] {\n const fuse = new Fuse(this.list(), {\n keys: [\n 'issue.id',\n 'issue.title.en-SG',\n 'evidence.text',\n 'impact.scopeItems.serviceId',\n 'impact.scopeItems.stationId',\n 'impact.scopeItems.fromStationId',\n 'impact.scopeItems.toStationId',\n ],\n includeScore: true,\n threshold: 0.8,\n });\n const searchResults = fuse.search({\n $or: [\n { 'issue.id': query },\n { 'issue.title.en-SG': query },\n { 'evidence.text': query },\n { 'impact.scopeItems.serviceId': query },\n { 'impact.scopeItems.stationId': query },\n { 'impact.scopeItems.fromStationId': query },\n { 'impact.scopeItems.toStationId': query },\n ] as Expression[],\n });\n const results: IssueBundle[] = [];\n for (const result of searchResults) {\n const bundle = this.get(result.item.issue.id);\n if (bundle == null) {\n continue;\n }\n results.push(bundle);\n }\n return results;\n }\n}\n"]}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { IssueBundle } from '
|
|
2
|
-
import type { CauseSubtype } from '
|
|
3
|
-
import type { FacilityEffect } from '
|
|
4
|
-
import type { Period } from '
|
|
5
|
-
import type { ServiceEffect } from '
|
|
6
|
-
import type { ServiceScope } from '
|
|
1
|
+
import type { IssueBundle } from '../../../schema/issue/bundle.js';
|
|
2
|
+
import type { CauseSubtype } from '../../../schema/issue/cause.js';
|
|
3
|
+
import type { FacilityEffect } from '../../../schema/issue/facilityEffect.js';
|
|
4
|
+
import type { Period } from '../../../schema/issue/period.js';
|
|
5
|
+
import type { ServiceEffect } from '../../../schema/issue/serviceEffect.js';
|
|
6
|
+
import type { ServiceScope } from '../../../schema/issue/serviceScope.js';
|
|
7
7
|
type BasisRef = {
|
|
8
8
|
evidenceId: string;
|
|
9
9
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deriveCurrentState.js","sourceRoot":"/","sources":["repo/issue/helpers/deriveCurrentState.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"deriveCurrentState.js","sourceRoot":"/","sources":["repo/issue/helpers/deriveCurrentState.ts"],"names":[],"mappings":"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","sourcesContent":["import { keyForAffectedEntity } from '../../../helpers/keyForAffectedEntity.js';\nimport type { IssueBundle } from '../../../schema/issue/bundle.js';\nimport type { CauseSubtype } from '../../../schema/issue/cause.js';\nimport type { FacilityEffect } from '../../../schema/issue/facilityEffect.js';\nimport type { Period } from '../../../schema/issue/period.js';\nimport type { ServiceEffect } from '../../../schema/issue/serviceEffect.js';\nimport type { ServiceScope } from '../../../schema/issue/serviceScope.js';\n\ntype BasisRef = {\n evidenceId: string;\n};\n\ntype ServiceProvenance = {\n effect?: BasisRef;\n scopes?: BasisRef;\n periods?: BasisRef;\n causes?: BasisRef;\n};\n\ntype ServiceState = {\n serviceId: string;\n effect: ServiceEffect | null;\n scopes: ServiceScope[];\n periods: Period[];\n causes: CauseSubtype[];\n};\n\ntype FacilityProvenance = {\n effect?: BasisRef;\n periods?: BasisRef;\n causes?: BasisRef;\n};\n\ntype FacilityState = {\n stationId: string;\n kind: 'lift' | 'escalator' | 'screen-door';\n effect: FacilityEffect | null;\n periods: Period[];\n causes: CauseSubtype[];\n};\n\nexport type IssueBundleState = {\n services: Record<string, ServiceState>;\n servicesProvenance: Record<string, ServiceProvenance>;\n facilities: Record<string, FacilityState>;\n facilitiesProvenance: Record<string, FacilityProvenance>;\n /**\n * The impact event ids that were used to derive the current state.\n */\n impactEventIds: string[];\n};\n\n/**\n * Derive the current state of an issue.\n * @param bundle\n * @returns\n */\nexport function deriveCurrentState(bundle: IssueBundle): IssueBundleState {\n const services: Record<string, ServiceState> = {};\n const servicesProvenance: Record<string, ServiceProvenance> = {};\n const facilities: Record<string, FacilityState> = {};\n const facilitiesProvenance: Record<string, FacilityProvenance> = {};\n const impactEventIds: {\n serviceEffect: string | null;\n serviceScopes: string | null;\n periods: string | null;\n causes: string | null;\n facilityEffect: string | null;\n } = {\n serviceEffect: null,\n serviceScopes: null,\n periods: null,\n causes: null,\n facilityEffect: null,\n };\n\n for (const impactEvent of bundle.impactEvents) {\n const key = keyForAffectedEntity(impactEvent.entity);\n\n switch (impactEvent.entity.type) {\n case 'service': {\n const currentState = services[key] ?? {\n serviceId: impactEvent.entity.serviceId,\n effect: null,\n scopes: [],\n periods: [],\n causes: [],\n };\n\n const currentProvenance = servicesProvenance[key] ?? {};\n\n switch (impactEvent.type) {\n case 'service_effects.set': {\n currentState.effect = impactEvent.effect;\n currentProvenance.effect = {\n evidenceId: impactEvent.basis.evidenceId,\n };\n impactEventIds.serviceEffect = impactEvent.id;\n break;\n }\n case 'periods.set': {\n currentState.periods = impactEvent.periods;\n currentProvenance.periods = {\n evidenceId: impactEvent.basis.evidenceId,\n };\n impactEventIds.periods = impactEvent.id;\n break;\n }\n case 'service_scopes.set': {\n currentState.scopes = impactEvent.serviceScopes;\n currentProvenance.scopes = {\n evidenceId: impactEvent.basis.evidenceId,\n };\n impactEventIds.serviceScopes = impactEvent.id;\n break;\n }\n case 'causes.set': {\n currentState.causes = impactEvent.causes;\n currentProvenance.causes = {\n evidenceId: impactEvent.basis.evidenceId,\n };\n impactEventIds.causes = impactEvent.id;\n break;\n }\n }\n\n services[key] = currentState;\n servicesProvenance[key] = currentProvenance;\n\n break;\n }\n case 'facility': {\n const currentState = facilities[key] ?? {\n stationId: impactEvent.entity.stationId,\n kind: impactEvent.entity.kind,\n effect: null,\n periods: [],\n };\n\n const currentProvenance = facilitiesProvenance[key] ?? {};\n\n switch (impactEvent.type) {\n case 'facility_effects.set': {\n currentState.effect = impactEvent.effect;\n currentProvenance.effect = {\n evidenceId: impactEvent.basis.evidenceId,\n };\n impactEventIds.facilityEffect = impactEvent.id;\n break;\n }\n case 'periods.set': {\n currentState.periods = impactEvent.periods;\n currentProvenance.periods = {\n evidenceId: impactEvent.basis.evidenceId,\n };\n impactEventIds.periods = impactEvent.id;\n break;\n }\n }\n\n facilities[key] = currentState;\n facilitiesProvenance[key] = currentProvenance;\n\n break;\n }\n default: {\n // @ts-expect-error fallback case\n throw new Error(`Unknown entity type: ${impactEvent.entity.type}`);\n }\n }\n }\n\n return {\n services,\n servicesProvenance,\n facilities,\n facilitiesProvenance,\n impactEventIds: Object.values(impactEventIds).filter(\n (id): id is string => id !== null,\n ),\n };\n}\n"]}
|