diff --git a/packages/sim-cli/src/contract/commands.test.ts b/packages/sim-cli/src/contract/commands.test.ts index 8be7fa00cf6..795635f75b8 100644 --- a/packages/sim-cli/src/contract/commands.test.ts +++ b/packages/sim-cli/src/contract/commands.test.ts @@ -4,6 +4,7 @@ import type { Command } from 'commander' import { describe, expect, it } from 'vitest' import { V2_OPERATIONS, type V2OperationName } from '../generated/v2-api' +import { HELP_EPILOGUE } from '../program' import { buildGeneratedCommands } from '../runtime/build' import { flagNameFor, flagSpecFor } from '../runtime/request' import type { OperationSpec } from '../runtime/types' @@ -193,4 +194,12 @@ describe('folder-path fields', () => { expect(paths).toContain('serviceId') expect(paths).toContain('providerId') }) + + it('mentions the variable that moves the profile files, since help names a path', () => { + // The epilogue states where the files live, and SIM_CONFIG_DIR moves both. + // Naming only ~/.sim made help wrong for anyone who had set it — including + // every CI job that points the CLI at a scratch directory. + expect(HELP_EPILOGUE).toContain('~/.sim/config') + expect(HELP_EPILOGUE).toContain('SIM_CONFIG_DIR') + }) }) diff --git a/packages/sim-cli/src/contract/commands.ts b/packages/sim-cli/src/contract/commands.ts index 66622e9d38a..43df9d729a6 100644 --- a/packages/sim-cli/src/contract/commands.ts +++ b/packages/sim-cli/src/contract/commands.ts @@ -259,7 +259,7 @@ export const CLI_CONTRACT: CliContract = { }, itemsPath: 'results', columns: [ - { header: 'score', path: 'similarity' }, + { header: 'score', path: 'similarity', format: 'score' }, { header: 'document', path: 'documentName' }, { header: 'chunk', path: 'chunkIndex' }, { header: 'content' }, diff --git a/packages/sim-cli/src/contract/types.ts b/packages/sim-cli/src/contract/types.ts index 5a8edbd1dd0..c763e8355ac 100644 --- a/packages/sim-cli/src/contract/types.ts +++ b/packages/sim-cli/src/contract/types.ts @@ -123,6 +123,10 @@ export interface ColumnSpec { * wire encoding for the human formats, so a folder no longer prints as * `/cli-test-a/nested%20one` in the same row as the `nested one` the server * put in the adjacent name column. + * + * `score` fixes a similarity to four decimals. The raw double arrives as + * `0.2818957269585687`, a nineteen-character column whose last dozen digits + * cannot separate one result from another. */ format?: | 'auto' @@ -134,6 +138,7 @@ export interface ColumnSpec { | 'count' | 'trace-count' | 'folder-path' + | 'score' } export interface BodyVariantSpec { diff --git a/packages/sim-cli/src/program.ts b/packages/sim-cli/src/program.ts index 249de03fa1a..a78817fb0ce 100644 --- a/packages/sim-cli/src/program.ts +++ b/packages/sim-cli/src/program.ts @@ -13,7 +13,8 @@ export const PROGRAM_DESCRIPTION = 'Talk to the Sim API from your terminal' export const HELP_EPILOGUE = ` Profiles work like the AWS CLI: settings live in ~/.sim/config, keys in -~/.sim/credentials (0600). Select one with -P, --profile, or SIM_PROFILE. +~/.sim/credentials (0600), or under SIM_CONFIG_DIR when it is set. Select one +with -P, --profile, or SIM_PROFILE. Examples: $ sim login Authorize the default profile diff --git a/packages/sim-cli/src/runtime/build.test.ts b/packages/sim-cli/src/runtime/build.test.ts index e867d7c9d49..eeee9c4019f 100644 --- a/packages/sim-cli/src/runtime/build.test.ts +++ b/packages/sim-cli/src/runtime/build.test.ts @@ -881,7 +881,9 @@ describe('contract-selected list rendering', () => { totalResults: 1, }) - expect(printed).toEqual(['0.91\tpolicy.md\t2\tRefunds are available for 30 days.']) + // Four decimals, fixed, like the `cost` column: a similarity is compared + // against its neighbours, so the width has to stay put down the column. + expect(printed).toEqual(['0.9100\tpolicy.md\t2\tRefunds are available for 30 days.']) }) it('renders row matches as rows', async () => { diff --git a/packages/sim-cli/src/runtime/result.test.ts b/packages/sim-cli/src/runtime/result.test.ts index 9bb79216577..4fdd8156d44 100644 --- a/packages/sim-cli/src/runtime/result.test.ts +++ b/packages/sim-cli/src/runtime/result.test.ts @@ -172,6 +172,29 @@ describe('a declared field that the API stops returning', () => { }) }) +describe('a similarity score, at a width a person can read', () => { + const results = { + results: [ + { similarity: 0.2818957269585687, documentName: 'a.md', chunkIndex: 0, content: 'x' }, + ], + } + const spec = CLI_CONTRACT.searchKnowledge as CommandSpec + + it('fixes the score to four decimals in the table', () => { + // The raw double is nineteen characters wide and its last dozen digits + // separate nothing: every row shares them to within a rounding error. + renderResult('searchKnowledge', 'table', results, spec) + const [, row] = tableLines() + expect(row).toContain('0.2819') + expect(row).not.toContain('0.2818957269585687') + }) + + it('leaves the full double in json, which is what a script compares', () => { + renderResult('searchKnowledge', 'json', results, spec) + expect(JSON.parse(logged[0]).results[0].similarity).toBe(0.2818957269585687) + }) +}) + describe('folder paths are shown by name, but piped in wire form', () => { const folders = [ { diff --git a/packages/sim-cli/src/runtime/result.ts b/packages/sim-cli/src/runtime/result.ts index 664a06d1ba9..40f30639216 100644 --- a/packages/sim-cli/src/runtime/result.ts +++ b/packages/sim-cli/src/runtime/result.ts @@ -80,6 +80,8 @@ function renderCell( return bool(value as boolean | null) case 'cost': return typeof value === 'number' ? `$${value.toFixed(4)}` : text(null) + case 'score': + return typeof value === 'number' ? value.toFixed(4) : text(null) case 'count': return Array.isArray(value) ? String(value.length) : text(null) case 'folder-path':