Remove stale app service references

This commit is contained in:
Andrey Avtomonov 2026-05-11 00:22:53 +02:00
parent e18e6ac8c4
commit 9530d33c00
10 changed files with 30 additions and 59 deletions

View file

@ -13,8 +13,8 @@ generates query workload under separate users, runs `ktx setup` with
- Docker with Compose v2
- Node and pnpm matching the KTX workspace
- `python-service/.venv` already created, or `KTX_SQL_ANALYSIS_URL` pointing at
a running service that exposes `/api/sql/analyze-for-fingerprint`
- `KTX_SQL_ANALYSIS_URL` or `KTX_DAEMON_URL` pointing at a running SQL-analysis
service that exposes `/api/sql/analyze-for-fingerprint`
## Run
@ -111,5 +111,5 @@ The manifest should have `dialect: "postgres"`, `degraded: true`,
- Missing grants: confirm `GRANT pg_read_all_stats TO ktx_reader;`.
- Empty templates: rerun `scripts/generate-workload.sh base` and keep
`--historic-sql-min-calls 2` for the smoke.
- SQL-analysis failures: set `KTX_SQL_ANALYSIS_URL` to the running service URL
or create `python-service/.venv` before running `scripts/smoke.sh`.
- SQL-analysis failures: set `KTX_SQL_ANALYSIS_URL` or `KTX_DAEMON_URL` to a
running service URL before running `scripts/smoke.sh`.

View file

@ -4,46 +4,23 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
EXAMPLE_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
KTX_ROOT="$(cd "$EXAMPLE_DIR/../.." && pwd)"
REPO_ROOT="$(cd "$KTX_ROOT/.." && pwd)"
COMPOSE_FILE="$EXAMPLE_DIR/docker-compose.yml"
PROJECT_PARENT="${KTX_POSTGRES_HISTORIC_PROJECT_PARENT:-$(mktemp -d)}"
PROJECT_DIR="$PROJECT_PARENT/postgres-historic-ktx"
KTX_BIN="$KTX_ROOT/packages/cli/dist/bin.js"
PYTHON_SERVICE_LOG="$PROJECT_PARENT/python-service.log"
PYTHON_SERVICE_PID=""
cleanup() {
if [[ -n "$PYTHON_SERVICE_PID" ]]; then
kill "$PYTHON_SERVICE_PID" >/dev/null 2>&1 || true
fi
if [[ "${KTX_POSTGRES_HISTORIC_KEEP_DOCKER:-0}" != "1" ]]; then
docker compose -f "$COMPOSE_FILE" down -v >/dev/null 2>&1 || true
fi
}
trap cleanup EXIT
start_sql_analysis_if_needed() {
if [[ -n "${KTX_SQL_ANALYSIS_URL:-}" ]]; then
require_sql_analysis_url() {
if [[ -n "${KTX_SQL_ANALYSIS_URL:-}" || -n "${KTX_DAEMON_URL:-}" ]]; then
return
fi
if [[ ! -d "$REPO_ROOT/python-service/.venv" ]]; then
echo "Set KTX_SQL_ANALYSIS_URL or create python-service/.venv before running this smoke." >&2
exit 1
fi
(
cd "$REPO_ROOT/python-service"
source .venv/bin/activate
uvicorn app.main:app --host 127.0.0.1 --port 18081 >"$PYTHON_SERVICE_LOG" 2>&1
) &
PYTHON_SERVICE_PID="$!"
export KTX_SQL_ANALYSIS_URL="http://127.0.0.1:18081"
for _ in $(seq 1 60); do
if curl -fsS "$KTX_SQL_ANALYSIS_URL/health" >/dev/null 2>&1; then
return
fi
sleep 1
done
echo "SQL analysis service did not become healthy. Log: $PYTHON_SERVICE_LOG" >&2
echo "Set KTX_SQL_ANALYSIS_URL or KTX_DAEMON_URL before running this smoke." >&2
exit 1
}
@ -111,7 +88,7 @@ NODE
cd "$KTX_ROOT"
pnpm --filter @ktx/context run build
pnpm --filter @ktx/cli run build
start_sql_analysis_if_needed
require_sql_analysis_url
docker compose -f "$COMPOSE_FILE" up -d --wait
"$EXAMPLE_DIR/scripts/generate-workload.sh" base

View file

@ -1,7 +1,7 @@
import { z } from 'zod';
// Literal vocabularies — kept in lockstep with the Python Pydantic model at
// python-service/ktx-sl/semantic_layer/models.py (SourceColumn / ColumnRole /
// python/ktx-sl/semantic_layer/models.py (SourceColumn / ColumnRole /
// ColumnVisibility / JoinDeclaration). If these diverge, YAMLs can pass
// TypeScript validation at ingest time but fail Python loading at query time.
const columnTypeValues = ['string', 'number', 'time', 'boolean'] as const;

View file

@ -2,7 +2,7 @@ import { describe, expect, it, vi } from 'vitest';
import { createHttpSqlAnalysisPort } from './http-sql-analysis-port.js';
describe('createHttpSqlAnalysisPort', () => {
it('calls the python-service fingerprint endpoint and maps snake_case response fields', async () => {
it('calls the SQL-analysis fingerprint endpoint and maps snake_case response fields', async () => {
const requestJson = vi.fn(async () => ({
fingerprint: 'fingerprint-template',
normalized_sql: 'SELECT * FROM analytics.orders WHERE status = ?',
@ -26,7 +26,7 @@ describe('createHttpSqlAnalysisPort', () => {
});
});
it('preserves python-service parse errors in the mapped result', async () => {
it('preserves SQL-analysis parse errors in the mapped result', async () => {
const requestJson = vi.fn(async () => ({
fingerprint: '',
normalized_sql: '',

View file

@ -151,7 +151,7 @@ export abstract class BaseTool<TInput extends ZodType = ZodType> {
}
}
},
// Send only markdown to LLM - frontend still receives full { markdown, structured } via stream
// Send only markdown to the LLM; tool callers still receive the structured output.
toModelOutput: ({ output }) => {
if (output && typeof output === 'object' && 'markdown' in output) {
return { type: 'content', value: [{ type: 'text', text: output.markdown as string }] };

View file

@ -14,7 +14,7 @@ function lowerProductName() {
describe('scanFileContent', () => {
it('rejects source imports from application directories', () => {
const serverAlias = '@' + 'server/contracts';
const pythonAppPath = 'python-service/' + 'app/api/endpoints/semantic_layer.py';
const pythonAppPath = `${['python', 'service'].join('-')}/app/api/endpoints/semantic_layer.py`;
const violations = [
...scanFileContent('packages/context/src/index.ts', `import { orpc } from '${serverAlias}';`),

View file

@ -4,7 +4,7 @@ import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { describe, it } from 'node:test';
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const ciWorkflowPath = resolve(repoRoot, '.github', 'workflows', 'ci.yml');
async function readCiWorkflowOrSkip(testContext) {
@ -21,7 +21,7 @@ async function readCiWorkflowOrSkip(testContext) {
}
describe('KTX CI artifact upload contract', () => {
it('uploads verified KTX package artifacts from check-ktx-subtree', async (testContext) => {
it('uploads verified KTX package artifacts from the standalone check job', async (testContext) => {
const workflow = await readCiWorkflowOrSkip(testContext);
if (workflow === null) {
return;
@ -29,42 +29,35 @@ describe('KTX CI artifact upload contract', () => {
assert.match(
workflow,
/name: Build ktx package artifacts and verify public smoke\s+run: cd ktx && pnpm run artifacts:build && pnpm run artifacts:verify-manifest && pnpm run artifacts:verify-demo\s+- name: Upload ktx package artifacts/s,
/name: Build and verify package artifacts\s+run: pnpm run artifacts:check\s+- name: Upload package artifacts/s,
);
assert.match(workflow, /uses: actions\/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f/);
assert.match(workflow, /name: ktx-package-artifacts-\$\{\{ github\.sha \}\}/);
assert.match(workflow, /ktx\/dist\/artifacts\/manifest\.json/);
assert.match(workflow, /ktx\/dist\/artifacts\/npm\/\*\.tgz/);
assert.match(workflow, /ktx\/dist\/artifacts\/python\/\*\.whl/);
assert.match(workflow, /ktx\/dist\/artifacts\/python\/\*\.tar\.gz/);
assert.match(workflow, /dist\/artifacts\/manifest\.json/);
assert.match(workflow, /dist\/artifacts\/npm\/\*\.tgz/);
assert.match(workflow, /dist\/artifacts\/python\/\*\.whl/);
assert.match(workflow, /dist\/artifacts\/python\/\*\.tar\.gz/);
assert.match(workflow, /if-no-files-found: error/);
assert.match(workflow, /retention-days: 7/);
});
it('runs packed demo artifact smoke on Linux and macOS', async (testContext) => {
it('runs TypeScript and Python checks in the standalone workflow', async (testContext) => {
const workflow = await readCiWorkflowOrSkip(testContext);
if (workflow === null) {
return;
}
assert.match(workflow, /check-ktx-packed-demo:/);
assert.match(workflow, /matrix:\s+os: \[ubuntu-latest, macos-latest\]/s);
assert.match(workflow, /name: Download ktx package artifacts/);
assert.match(workflow, /path: ktx\/dist\/artifacts/);
assert.match(workflow, /run: cd ktx && pnpm run artifacts:verify-demo/);
assert.match(workflow, /run: pnpm run check/);
assert.match(workflow, /run: uv sync --all-packages/);
assert.match(workflow, /run: uv run pytest/);
});
it('includes packed demo artifact smoke in ci-success', async (testContext) => {
it('does not depend on host application CI jobs', async (testContext) => {
const workflow = await readCiWorkflowOrSkip(testContext);
if (workflow === null) {
return;
}
assert.match(
workflow,
/needs: \[check-ktx-subtree, check-ktx-packed-demo, build-python-service, test-server, build-frontend, run-pre-commit, build-docker-images\]/,
);
assert.match(workflow, /needs\.check-ktx-packed-demo\.result.*== "failure"/);
assert.match(workflow, /needs\.check-ktx-packed-demo\.result.*== "cancelled"/);
assert.doesNotMatch(workflow, /build-python-service|test-server|build-frontend|build-docker-images/);
});
});

View file

@ -31,11 +31,10 @@ describe('Conductor workspace scripts', () => {
it('runs the KTX daemon on the documented fixed local port', async () => {
const runScript = await readText('scripts/conductor-run.sh');
const legacyServerPackagePattern = new RegExp(`@${['kae', 'lio'].join('')}/server|python-service|npx`);
assert.match(runScript, /pnpm run build/);
assert.match(runScript, /source \.venv\/bin\/activate/);
assert.match(runScript, /uv run ktx-daemon serve-http --host 127\.0\.0\.1 --port 8765/);
assert.doesNotMatch(runScript, legacyServerPackagePattern);
assert.doesNotMatch(runScript, /\bnpx\b/);
});
});

View file

@ -66,6 +66,8 @@ describe('standalone example docs', () => {
assert.match(smoke, /assert_manifest "\$FIRST_MANIFEST" true/);
assert.match(smoke, /assert_manifest "\$SECOND_MANIFEST" false/);
assert.match(smoke, /assert_manifest "\$RESET_MANIFEST" true/);
assert.doesNotMatch(readme, /python-service/);
assert.doesNotMatch(smoke, /python-service|PYTHON_SERVICE|REPO_ROOT/);
});
it('lists every published TypeScript package in the package root README', async () => {

View file

@ -9,7 +9,7 @@ function commandKeys(files) {
describe('precommit-check', () => {
it('skips files outside ktx', () => {
assert.deepEqual(commandKeys(['server/src/app.ts']), []);
assert.deepEqual(commandKeys(['outside-workspace/src/app.ts']), []);
});
it('runs only the touched package checks for package code', () => {