@thomasminh1995/depverdict 0.6.0-alpha.1
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/LICENSE +21 -0
- package/README.md +518 -0
- package/bin/depverdict.js +7 -0
- package/bin/upgradelens.js +7 -0
- package/docs/GR-01-Semantic-Grounding-Failure-Analysis.md +309 -0
- package/docs/GR-02-Versioned-Action-Evaluation-Criteria.md +187 -0
- package/docs/GR-03-Extractive-Contract-Safety-Experiment.md +242 -0
- package/docs/GR-04-Versioned-Production-Extractive-Contract.md +154 -0
- package/docs/IA-01-Repository-Usage-Discovery.md +122 -0
- package/docs/IA-02-Repository-Impact-Analysis.md +118 -0
- package/docs/IA-03-Repository-Impact-Evidence.md +160 -0
- package/docs/IA-04-CLI-Orchestration.md +235 -0
- package/docs/IA-05-Real-Provider-Validation.md +235 -0
- package/docs/IA-05-VinGrade-Validation.md +339 -0
- package/docs/MVP-01.md +48 -0
- package/docs/MVP-02-Architecture.md +536 -0
- package/docs/MVP-02-CLI-HTTP-Runtime.md +50 -0
- package/docs/MVP-02-HTTP-Lifecycle.md +37 -0
- package/docs/MVP-02-Knowledge-Manifest-Generation.md +75 -0
- package/docs/MVP-02-Knowledge-Manifest.md +279 -0
- package/docs/MVP-02-Knowledge-Research-Orchestration.md +62 -0
- package/docs/MVP-02-Knowledge-Store.md +87 -0
- package/docs/MVP-02-PyPI-Registry-Adapter.md +97 -0
- package/docs/MVP-02-Research-Planning.md +196 -0
- package/docs/MVP-02-Source-Provenance.md +81 -0
- package/docs/MVP-02-npm-Registry-Adapter.md +99 -0
- package/docs/RR-01-End-to-End-and-Real-Provider-Validation.md +393 -0
- package/docs/RR-01-RERUN-Extractive-Contract-Validation.md +566 -0
- package/docs/RR-02-Full-Product-Workflow-and-Developer-CLI-UX-Review.md +35 -0
- package/docs/RR02-FIX-01-Persistent-Qualification-Resolution.md +32 -0
- package/docs/RR02-FIX-02-Stage-aware-CLI-Progress-and-Heartbeat.md +28 -0
- package/docs/RR02-FIX-03-npm-Capture-Evidence-Exclusion.md +33 -0
- package/docs/RR02-FIX-03A-Complete-Package-Exclusion-and-Evidence-Commit.md +223 -0
- package/docs/RR02-FIX-04-Event-loop-safe-Heartbeat.md +393 -0
- package/docs/RR02-FIX-05-Materialize-Persisted-Qualification.md +207 -0
- package/docs/RR02-RERUN-CLI-Qualification-Progress-UX-and-Package-Validation.md +342 -0
- package/docs/VinGrade-MVP-02-Validation.md +480 -0
- package/docs/VinGrade-RC02-Live-Validation.md +273 -0
- package/docs/ai-capability-discovery.md +573 -0
- package/docs/ai-engineering-production-readiness.md +390 -0
- package/docs/ai-engineering-review.md +251 -0
- package/docs/ai-runtime-governance-discovery.md +754 -0
- package/docs/architecture-overview.md +78 -0
- package/docs/cli-progress.md +111 -0
- package/docs/decisions/diff-01-brand-distribution-identity.md +450 -0
- package/docs/decisions/diff-02-identity-compatibility-contract.md +274 -0
- package/docs/decisions/diff-03-repository-docs-community-migration.md +165 -0
- package/docs/decisions/diff-04-release-evidence-gap-acceptance.md +110 -0
- package/docs/discovery/mvp-05-ai-migration-planning-discovery.md +350 -0
- package/docs/gateway-runtime-discovery.md +614 -0
- package/docs/live-ai-validation.md +299 -0
- package/docs/migration-planning-qualification-resolution.md +99 -0
- package/docs/migrations/upgradelens-to-depverdict.md +97 -0
- package/docs/mp-r03-deterministic-upgrade-decision-architecture.md +135 -0
- package/docs/mp-r04-evidence-bounded-migration-handoff-architecture.md +116 -0
- package/docs/mp-r05-product-completion-and-decision-first-cli-architecture.md +162 -0
- package/docs/mvp-05-deterministic-context-runtime.md +127 -0
- package/docs/mvp-05-migration-checklist-contract.md +166 -0
- package/docs/mvp-05-migration-checklist-orchestration.md +105 -0
- package/docs/mvp-05-migration-evaluation-and-qualification.md +135 -0
- package/docs/mvp-05-provider-neutral-generator.md +79 -0
- package/docs/ollama-local-smoke-validation.md +156 -0
- package/docs/openai-compatible-runtime-discovery.md +789 -0
- package/docs/openrouter-one-dependency-validation.md +205 -0
- package/docs/oss-02-package-guard-hardening-architecture.md +131 -0
- package/docs/oss-04-public-ci-package-metadata-architecture.md +142 -0
- package/docs/package-content-policy.md +98 -0
- package/docs/releases/v0.5.0-technical-preview.md +114 -0
- package/docs/releases/v0.6.0-alpha.1-depverdict-preview.md +101 -0
- package/docs/reviews/diff-02-identity-contract-compatibility.md +413 -0
- package/docs/reviews/diff-03-repository-docs-community-migration.md +364 -0
- package/docs/reviews/diff-04-depverdict-distribution-identity-readiness-rereview.md +453 -0
- package/docs/reviews/diff-04-fix-post-rename-identity-release-remediation.md +310 -0
- package/docs/reviews/diff-05-final-preview-distribution-qualification.md +561 -0
- package/docs/reviews/mvp-05-final-product-value-workflow-rereview.md +471 -0
- package/docs/reviews/mvp-05-product-workflow-review.md +605 -0
- package/docs/reviews/oss-01-duplicate-artifact-investigation-cleanup.md +411 -0
- package/docs/reviews/oss-02-package-guard-hardening.md +303 -0
- package/docs/reviews/oss-03-community-scaffolding.md +378 -0
- package/docs/reviews/oss-04-public-ci-package-metadata.md +439 -0
- package/docs/reviews/oss-05-technical-preview-qualification.md +482 -0
- package/docs/reviews/upgradelens-vs-upgradedepdetective-source-comparison.md +355 -0
- package/docs/reviews/v0.5.0-pre-release-smoke.md +263 -0
- package/docs/reviews/v0.5.0-version-bump-release-verification.md +306 -0
- package/docs/runtime-contract-discovery.md +521 -0
- package/docs/structured-output-compatibility-report.md +100 -0
- package/docs/ts-fix-01-exact-duplicate-occurrence-target-selection-architecture.md +111 -0
- package/docs/version-analysis-architecture.md +827 -0
- package/eval/README.md +86 -0
- package/eval/datasets/generic/declared-constraint.json +59 -0
- package/eval/datasets/generic/evidence-conflict.json +73 -0
- package/eval/datasets/generic/major-breaking-release.json +66 -0
- package/eval/datasets/generic/missing-evidence.json +46 -0
- package/eval/datasets/generic/patch-release-low.json +59 -0
- package/eval/datasets/node/axios-patch-low.json +59 -0
- package/eval/datasets/node/react-major-breaking.json +66 -0
- package/eval/datasets/node/react-minor-compatibility.json +66 -0
- package/eval/datasets/python/fastapi-deprecation.json +66 -0
- package/eval/datasets/python/pydantic-major-breaking.json +66 -0
- package/eval/migration-planning/golden-dataset-v2.json +306 -0
- package/eval/migration-planning/golden-dataset.json +214 -0
- package/eval/schemas/expected-result.schema.json +88 -0
- package/eval/schemas/golden-case.schema.json +181 -0
- package/package.json +57 -0
- package/schemas/ai-scorecard.schema.json +132 -0
- package/schemas/benchmark-report.schema.json +189 -0
- package/schemas/benchmark.schema.json +55 -0
- package/schemas/capability-profile.schema.json +44 -0
- package/schemas/conformance-report.schema.json +150 -0
- package/schemas/deployment-profile.schema.json +64 -0
- package/schemas/evaluation-report.schema.json +158 -0
- package/schemas/knowledge-evidence-bundle.schema.json +150 -0
- package/schemas/knowledge-manifest.schema.json +548 -0
- package/schemas/metrics.schema.json +178 -0
- package/schemas/migration-checklist-extractive-candidate.schema.json +42 -0
- package/schemas/migration-checklist.schema.json +706 -0
- package/schemas/migration-evaluation-dataset-v2.schema.json +208 -0
- package/schemas/migration-evaluation-dataset.schema.json +204 -0
- package/schemas/migration-planning-qualification-record.schema.json +234 -0
- package/schemas/project-manifest.schema.json +308 -0
- package/schemas/qualification-record.schema.json +56 -0
- package/schemas/repository-impact-evidence.schema.json +232 -0
- package/schemas/repository-impact.schema.json +202 -0
- package/schemas/upgrade-decision.schema.json +273 -0
- package/schemas/usage-index.schema.json +179 -0
- package/schemas/version-analysis.schema.json +449 -0
- package/src/ai-runtime-debug.js +325 -0
- package/src/ai-runtime-error.js +42 -0
- package/src/ai-runtime.js +174 -0
- package/src/ai-scorecard.js +204 -0
- package/src/ai-version-analysis.js +484 -0
- package/src/artifact-root-compatibility.js +91 -0
- package/src/benchmark-report.js +111 -0
- package/src/benchmark-runner.js +191 -0
- package/src/canonical-json.js +69 -0
- package/src/cli.js +1299 -0
- package/src/conformance-report.js +158 -0
- package/src/conformance-runner.js +253 -0
- package/src/constants.js +73 -0
- package/src/cooperative-scheduler.js +79 -0
- package/src/dependencies.js +44 -0
- package/src/dependency-ai-context.js +625 -0
- package/src/detectors.js +253 -0
- package/src/discovery.js +234 -0
- package/src/ecosystem-version-adapter.js +294 -0
- package/src/environment-compatibility.js +77 -0
- package/src/evaluation-comparator.js +158 -0
- package/src/evaluation-report.js +76 -0
- package/src/evaluation-runner.js +248 -0
- package/src/evidence-source-adapter.js +472 -0
- package/src/files.js +89 -0
- package/src/governance-diagnostics.js +64 -0
- package/src/governance-loader.js +63 -0
- package/src/governance-metadata.js +346 -0
- package/src/governance-validator.js +360 -0
- package/src/http/bounded-fetch.js +278 -0
- package/src/http/cli-http-runtime.js +44 -0
- package/src/impact/input-loader.js +157 -0
- package/src/impact/matcher.js +40 -0
- package/src/impact/repository-impact.js +199 -0
- package/src/impact/runtime.js +24 -0
- package/src/impact/status.js +62 -0
- package/src/impact/writer.js +30 -0
- package/src/impact-evidence/input-loader.js +202 -0
- package/src/impact-evidence/repository-impact-evidence.js +234 -0
- package/src/impact-evidence/runtime.js +16 -0
- package/src/impact-evidence/writer.js +30 -0
- package/src/index.js +563 -0
- package/src/installed-version-baseline.js +196 -0
- package/src/knowledge-cache.js +324 -0
- package/src/knowledge-evidence-bundle.js +101 -0
- package/src/knowledge-evidence-producer.js +233 -0
- package/src/knowledge-manifest-builder.js +188 -0
- package/src/knowledge-manifest-writer.js +32 -0
- package/src/knowledge-manifest.js +255 -0
- package/src/knowledge-research.js +615 -0
- package/src/metrics-engine.js +205 -0
- package/src/migration-checklist/ai-candidate.js +320 -0
- package/src/migration-checklist/assembler.js +37 -0
- package/src/migration-checklist/context-runtime.js +828 -0
- package/src/migration-checklist/evaluation/action-criteria.js +244 -0
- package/src/migration-checklist/evaluation/comparator-v2.js +332 -0
- package/src/migration-checklist/evaluation/comparator.js +279 -0
- package/src/migration-checklist/evaluation/dataset-v2.js +227 -0
- package/src/migration-checklist/evaluation/dataset.js +336 -0
- package/src/migration-checklist/evaluation/extractive-fixtures-v2.js +148 -0
- package/src/migration-checklist/evaluation/metrics-v2.js +226 -0
- package/src/migration-checklist/evaluation/metrics.js +158 -0
- package/src/migration-checklist/evaluation/qualification-v2.js +321 -0
- package/src/migration-checklist/evaluation/qualification.js +239 -0
- package/src/migration-checklist/evaluation/runner-v2.js +294 -0
- package/src/migration-checklist/evaluation/runner.js +194 -0
- package/src/migration-checklist/evaluation/scorecard-v2.js +106 -0
- package/src/migration-checklist/evaluation/scorecard.js +86 -0
- package/src/migration-checklist/extractive-candidate.js +166 -0
- package/src/migration-checklist/extractive-prompt.js +62 -0
- package/src/migration-checklist/generator.js +702 -0
- package/src/migration-checklist/grounding-policy.js +117 -0
- package/src/migration-checklist/input-loader.js +613 -0
- package/src/migration-checklist/migration-checklist.js +635 -0
- package/src/migration-checklist/presentation.js +292 -0
- package/src/migration-checklist/progress.js +96 -0
- package/src/migration-checklist/prompt.js +83 -0
- package/src/migration-checklist/qualification-guard.js +462 -0
- package/src/migration-checklist/qualification-resolution.js +122 -0
- package/src/migration-checklist/qualification-store.js +225 -0
- package/src/migration-checklist/runtime.js +205 -0
- package/src/migration-checklist/verification.js +134 -0
- package/src/migration-checklist/writer.js +35 -0
- package/src/openai-compatible-provider.js +451 -0
- package/src/orchestration/failure-log.js +32 -0
- package/src/orchestration/pipeline.js +200 -0
- package/src/orchestration/progress-events.js +337 -0
- package/src/orchestration/progress-reporter.js +131 -0
- package/src/orchestration/text-writer.js +22 -0
- package/src/portable.js +13 -0
- package/src/product-completion.js +249 -0
- package/src/project-manifest-input.js +90 -0
- package/src/project-manifest.js +141 -0
- package/src/python-requirements.js +137 -0
- package/src/registry/npm-packument.js +256 -0
- package/src/registry/npm-registry-adapter.js +262 -0
- package/src/registry/pypi-project.js +300 -0
- package/src/registry/pypi-registry-adapter.js +235 -0
- package/src/registry/sanitize-registry-body.js +51 -0
- package/src/renderers/console.js +160 -0
- package/src/renderers/impact-presentation.js +278 -0
- package/src/renderers/markdown.js +172 -0
- package/src/research-plan.js +455 -0
- package/src/runtime-conformance.js +275 -0
- package/src/source-provenance.js +393 -0
- package/src/source-url.js +62 -0
- package/src/structured-output-schema.js +66 -0
- package/src/target-selector.js +306 -0
- package/src/upgrade-decision/input-loader.js +107 -0
- package/src/upgrade-decision/presentation.js +43 -0
- package/src/upgrade-decision/runtime.js +21 -0
- package/src/upgrade-decision/upgrade-decision.js +626 -0
- package/src/upgrade-decision/writer.js +30 -0
- package/src/usage/analyzer-registry.js +63 -0
- package/src/usage/coverage.js +116 -0
- package/src/usage/input-loader.js +139 -0
- package/src/usage/js/analyzer.js +240 -0
- package/src/usage/js/parser.js +21 -0
- package/src/usage/runtime.js +187 -0
- package/src/usage/scope.js +44 -0
- package/src/usage/source-files.js +50 -0
- package/src/usage/usage-index.js +217 -0
- package/src/usage/writer.js +31 -0
- package/src/version-analysis-loader.js +203 -0
- package/src/version-analysis-manifest.js +314 -0
- package/src/version-analysis-writer.js +30 -0
|
@@ -0,0 +1,789 @@
|
|
|
1
|
+
# LR-00 — OpenAI-Compatible Runtime Architecture Discovery
|
|
2
|
+
|
|
3
|
+
Ngày khảo sát: 2026-07-15
|
|
4
|
+
|
|
5
|
+
Phạm vi: discovery kiến trúc. Tài liệu này không thay đổi runtime behavior, không triển khai adapter, không thêm dependency, không dùng API key và không gọi model.
|
|
6
|
+
|
|
7
|
+
## 1. Executive Summary
|
|
8
|
+
|
|
9
|
+
Generic HTTP provider hiện tại **không tương thích trực tiếp** với OpenAI-compatible protocol. CLI mặc định gửi `POST` tới nguyên URL trong `UPGRADELENS_AI_ENDPOINT`, body `{ prompt, outputSchema }`, không gửi model, và đọc kết quả từ `body.output`. OpenAI-compatible Chat Completions cần `model`, `messages`, thường thêm `response_format`, và trả nội dung trong `choices[0].message.content`. Vì vậy trạng thái hiện tại của Ollama, vLLM, LiteLLM Proxy, LM Studio, OpenRouter và OpenAI API đều là `REQUIRES_MAPPING` nếu dùng nguyên cấu hình CLI hiện có.
|
|
10
|
+
|
|
11
|
+
Kiến trúc nhỏ nhất nên là:
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
UpgradeLens AI core
|
|
15
|
+
↓
|
|
16
|
+
AiRuntime
|
|
17
|
+
↓
|
|
18
|
+
OpenAiCompatibleProvider
|
|
19
|
+
↓
|
|
20
|
+
User-selected Chat Completions endpoint
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Adapter nên dùng protocol Chat Completions làm baseline, không dùng OpenAI SDK và không chứa nhánh theo vendor. Một cấu hình endpoint nhỏ gồm full endpoint URL, model, optional Authorization và capability `structuredOutput` là đủ. Chưa cần registry, dynamic loading, plugin SDK hoặc endpoint profile theo tên vendor. Responses API có thể được bổ sung sau nhưng không mở khóa thêm giá trị cho use case một request/response hiện tại.
|
|
24
|
+
|
|
25
|
+
Structured output nên theo thứ tự `jsonSchema → jsonMode → promptOnly`, nhưng fallback phải do capability/config đã biết quyết định, không âm thầm retry một request khác sau mọi lỗi `400`. Kết quả luôn phải được parse, validate lại bằng `AI_VERSION_ANALYSIS_CANDIDATE_SCHEMA`, rồi đi qua trust validation hiện tại. Native schema là ràng buộc lúc sinh; local schema và trust layer mới là authority.
|
|
26
|
+
|
|
27
|
+
Runtime đầu tiên nên là **Ollama local qua OpenAI-compatible Chat Completions**, dùng model đã có trên máy contributor. Lý do: Ollama là MIT, chạy local, đã cài trên máy test, không cần cloud account, hỗ trợ `/v1/chat/completions`, `response_format` và JSON Schema. Smoke validation chỉ chứng minh transport/schema/trust pipeline; nó không chứng minh chất lượng phân tích production của `qwen3:latest` hoặc `llama3:latest`.
|
|
28
|
+
|
|
29
|
+
## 2. Current UpgradeLens Runtime Contract
|
|
30
|
+
|
|
31
|
+
### 2.1 Internal `AiRuntime` contract
|
|
32
|
+
|
|
33
|
+
Contract được định nghĩa trong `src/ai-runtime.js:1-27` và được gọi thực tế tại `src/ai-version-analysis.js:367-430`:
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
await runtime.generateStructured({
|
|
37
|
+
runId,
|
|
38
|
+
contextId: context.contextId,
|
|
39
|
+
promptVersion,
|
|
40
|
+
context,
|
|
41
|
+
outputSchema
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
| Field | Nguồn và behavior thực tế |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| `runId` | Mặc định `run:${context.contextId}` trong `analyzeDependencyAiContext`; được truyền qua provider nhưng generic HTTP default body loại bỏ field này. |
|
|
48
|
+
| `contextId` | Digest ổn định của Dependency AI Context; cũng bị generic default body loại khỏi HTTP request. |
|
|
49
|
+
| `promptVersion` | Mặc định `VERSION_ANALYSIS_PROMPT_VERSION = "1"`; prompt builder chép vào `prompt.promptVersion`. |
|
|
50
|
+
| `context` | Dependency context đầy đủ: lineage, dependency identity, version facts, evidence đã chọn và metadata. |
|
|
51
|
+
| `outputSchema` | `AI_VERSION_ANALYSIS_CANDIDATE_SCHEMA`, JSON Schema draft 2020-12, strict object và không cho model viết lại deterministic dependency/version facts. |
|
|
52
|
+
|
|
53
|
+
`validateAiRuntime` chỉ kiểm tra runtime có function `generateStructured`. Nó không kiểm tra capability, timeout, model hay provider.
|
|
54
|
+
|
|
55
|
+
Kết quả nội bộ được mô tả là:
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
{
|
|
59
|
+
output: unknown,
|
|
60
|
+
provider: string,
|
|
61
|
+
model: string,
|
|
62
|
+
latencyMs: number,
|
|
63
|
+
usage?: { inputTokens?: number, outputTokens?: number }
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`createProviderAiRuntime` tại `src/ai-runtime.js:49-85` chịu trách nhiệm:
|
|
68
|
+
|
|
69
|
+
1. dựng prompt từ `context`, `outputSchema`, `promptVersion`;
|
|
70
|
+
2. gọi provider với toàn bộ request cộng thêm `prompt`;
|
|
71
|
+
3. lấy `provider`/`model` từ response, rồi fallback sang metadata tĩnh của provider;
|
|
72
|
+
4. đo latency nếu provider không trả latency;
|
|
73
|
+
5. chuyển tiếp nguyên `response.usage` mà không normalize field.
|
|
74
|
+
|
|
75
|
+
`analyzeDependencyAiContext` chỉ dùng `runtimeResult.output`; provider/model/latency/usage không được chép vào trusted analysis result hay portable Version Analysis artifact. Evaluation report nhận model metadata riêng từ CLI/injection, còn Benchmark Runner quan sát runtime response qua wrapper trước khi analysis core loại metadata này. Đây là ranh giới chủ ý giữa portable artifact và execution telemetry, nhưng cũng có nghĩa model/provider từ HTTP response hiện không được audit trong artifact.
|
|
76
|
+
|
|
77
|
+
Internal provider contract không có typedef riêng nhưng behavior thực tế là:
|
|
78
|
+
|
|
79
|
+
```js
|
|
80
|
+
provider.generateStructured({
|
|
81
|
+
runId,
|
|
82
|
+
contextId,
|
|
83
|
+
promptVersion,
|
|
84
|
+
context,
|
|
85
|
+
outputSchema,
|
|
86
|
+
prompt: { promptVersion, system, user }
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Provider phải trả tối thiểu `{ output }`; `provider`, `model`, `latencyMs`, `usage` là metadata được runtime fallback hoặc bổ sung.
|
|
91
|
+
|
|
92
|
+
### 2.2 Prompt và schema flow
|
|
93
|
+
|
|
94
|
+
`buildVersionAnalysisPrompt` tại `src/ai-version-analysis.js:193-221` trả object, không phải một string:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"promptVersion": "1",
|
|
99
|
+
"system": "You are UpgradeLens AI Version Analysis.\n...",
|
|
100
|
+
"user": "Rules:\n...\nStructured output schema:\n{...}\n\nDependency AI Context:\n{...}"
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Schema được dùng hai lần ở boundary hiện tại:
|
|
105
|
+
|
|
106
|
+
- serialize trực tiếp vào `prompt.user` để ràng buộc bằng prompt;
|
|
107
|
+
- truyền riêng dưới field `outputSchema` tới provider.
|
|
108
|
+
|
|
109
|
+
CLI không chuyển `outputSchema` thành `response_format`. Vì vậy schema hiện chỉ được gửi như generic JSON data và prompt text; nó chưa kích hoạt native structured-output của OpenAI-compatible endpoint.
|
|
110
|
+
|
|
111
|
+
Sau response, `analyzeDependencyAiContext`:
|
|
112
|
+
|
|
113
|
+
1. parse `runtimeResult.output` nếu output là string;
|
|
114
|
+
2. validate bằng Ajv và `AI_VERSION_ANALYSIS_CANDIDATE_SCHEMA`;
|
|
115
|
+
3. chạy `trustValidateAiVersionAnalysisCandidate` để allowlist evidence refs, loại claims không có evidence, phát hiện URL ngoài evidence và áp dụng human-review policy.
|
|
116
|
+
|
|
117
|
+
Local schema validation và trust validation đã là bắt buộc trong core và phải giữ nguyên với mọi runtime mới.
|
|
118
|
+
|
|
119
|
+
### 2.3 Current generic HTTP contract
|
|
120
|
+
|
|
121
|
+
`createHttpJsonAiProvider` tại `src/ai-runtime.js:93-146` có mapper tùy biến, nhưng CLI tại `src/cli.js:323-340` không cung cấp mapper. Do đó production CLI dùng đúng default sau.
|
|
122
|
+
|
|
123
|
+
Request tối giản theo code hiện tại:
|
|
124
|
+
|
|
125
|
+
```http
|
|
126
|
+
POST <exact UPGRADELENS_AI_ENDPOINT value>
|
|
127
|
+
content-type: application/json
|
|
128
|
+
authorization: <exact UPGRADELENS_AI_AUTHORIZATION value, if set>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"prompt": {
|
|
134
|
+
"promptVersion": "1",
|
|
135
|
+
"system": "...",
|
|
136
|
+
"user": "...schema and dependency context..."
|
|
137
|
+
},
|
|
138
|
+
"outputSchema": {
|
|
139
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
140
|
+
"type": "object"
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Các điểm cần lưu ý:
|
|
146
|
+
|
|
147
|
+
- `UPGRADELENS_AI_ENDPOINT` hiện là **full endpoint URL**, không phải base URL; code không nối `/v1/chat/completions`.
|
|
148
|
+
- `UPGRADELENS_AI_MODEL` chỉ gắn nhãn metadata trên response; **không được gửi trong body**.
|
|
149
|
+
- `UPGRADELENS_AI_PROVIDER` cũng chỉ là metadata/error label.
|
|
150
|
+
- `UPGRADELENS_AI_AUTHORIZATION` là nguyên giá trị header; người dùng hiện phải tự thêm scheme như `Bearer `.
|
|
151
|
+
- Header caller truyền vào có thể ghi đè `content-type` do spread order.
|
|
152
|
+
- Không có `Accept`, user-agent riêng cho AI, redirect policy hay content-type validation.
|
|
153
|
+
|
|
154
|
+
Success response mà default extractor mong đợi:
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"output": {
|
|
159
|
+
"summary": "...",
|
|
160
|
+
"summaryEvidenceRefs": [],
|
|
161
|
+
"riskLevel": "unknown",
|
|
162
|
+
"riskEvidenceRefs": [],
|
|
163
|
+
"findings": []
|
|
164
|
+
},
|
|
165
|
+
"usage": {
|
|
166
|
+
"inputTokens": 100,
|
|
167
|
+
"outputTokens": 25
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
`output` cũng có thể là JSON string. `body.usage` được chuyển tiếp nguyên trạng; provider không map OpenAI `prompt_tokens`/`completion_tokens` sang internal `inputTokens`/`outputTokens`.
|
|
173
|
+
|
|
174
|
+
### 2.4 Timeout, retry, response size và errors hiện tại
|
|
175
|
+
|
|
176
|
+
Behavior xác minh từ `src/ai-runtime.js:118-145`:
|
|
177
|
+
|
|
178
|
+
- không có timeout hoặc `AbortSignal`;
|
|
179
|
+
- không retry;
|
|
180
|
+
- gọi `response.text()` không giới hạn kích thước;
|
|
181
|
+
- đọc toàn bộ body trước khi kiểm tra `response.ok`;
|
|
182
|
+
- non-2xx chỉ tạo `Error("AI provider <provider> returned HTTP <status>.")`, bỏ error body và headers;
|
|
183
|
+
- success body không phải JSON tạo error `returned invalid JSON`;
|
|
184
|
+
- không validate response envelope; thiếu `body.output` chỉ trở thành `undefined` và bị candidate schema validation bắt ở lớp sau;
|
|
185
|
+
- network/abort errors từ `fetch` đi qua nguyên trạng.
|
|
186
|
+
|
|
187
|
+
Tại `src/ai-version-analysis.js:407-429`, mọi exception ngoại trừ sentinel `INVALID_JSON` đều bị biểu diễn trong analysis result như `OUTPUT_SCHEMA_INVALID`. Vì vậy auth, timeout, transport, model-not-found và provider error hiện chưa phân biệt được và CLI có thể báo một lỗi schema gây hiểu nhầm.
|
|
188
|
+
|
|
189
|
+
Repository đã có bounded transport tốt hơn cho registry tại `src/http/bounded-fetch.js` (timeout, response byte limit, redirect `error`, sanitized errors), nhưng AI provider chưa dùng các guardrail tương đương.
|
|
190
|
+
|
|
191
|
+
### 2.5 CLI, evaluation và benchmark wiring
|
|
192
|
+
|
|
193
|
+
Không có AI env constants trong `src/constants.js`; bốn tên env hiện là string literal trong `src/cli.js`:
|
|
194
|
+
|
|
195
|
+
```text
|
|
196
|
+
UPGRADELENS_AI_ENDPOINT
|
|
197
|
+
UPGRADELENS_AI_PROVIDER
|
|
198
|
+
UPGRADELENS_AI_MODEL
|
|
199
|
+
UPGRADELENS_AI_AUTHORIZATION
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Precedence thực tế:
|
|
203
|
+
|
|
204
|
+
- `analyze-version`: `io.aiRuntime` được ưu tiên; nếu không có và ít nhất một context có evidence thì dùng env runtime; không evidence thì không tạo runtime.
|
|
205
|
+
- `eval`: `io.aiRuntime` → env endpoint → `golden-fake`.
|
|
206
|
+
- `benchmark`: mỗi run `goldenFake` không cần runtime; `environment` gọi default env runtime; type khác phải được inject bằng `benchmarkRuntimeFactory`.
|
|
207
|
+
- `io.env` thay hoàn toàn `process.env` khi test/injection cung cấp nó.
|
|
208
|
+
|
|
209
|
+
Evaluation Runner (`src/evaluation-runner.js:193-226`) gọi cùng `analyzeDependencyAiContext`, cùng schema và trust path. Benchmark Runner (`src/benchmark-runner.js:64-175`) wrap runtime để thu latency, token usage và cost. Collector nhận `tokenUsage`, `usage.totalTokens` hoặc `usage.total_tokens`; nó chưa cộng `inputTokens + outputTokens` và current generic provider chưa normalize usage.
|
|
210
|
+
|
|
211
|
+
Tests xác nhận contract thay vì chỉ tên function:
|
|
212
|
+
|
|
213
|
+
- `test/ai-version-analysis.test.js:362-390`: prompt được build và provider nhận `prompt.promptVersion`.
|
|
214
|
+
- `test/ai-version-analysis.test.js:392-419`: generic HTTP mapper là configurable và cố ý không khóa vào OpenAI shape.
|
|
215
|
+
- `test/evaluation-runner.test.js`: fake runtime đi qua analysis/trust/evaluation path.
|
|
216
|
+
- `test/benchmark-runner.test.js`: runtime response metadata được collector tổng hợp.
|
|
217
|
+
- `test/version-analysis-manifest.test.js`: CLI inject runtime, skip/no-call cases và per-dependency failure behavior.
|
|
218
|
+
|
|
219
|
+
`docs/live-ai-validation.md` ghi nhận pipeline real-evidence dừng đúng tại missing runtime configuration. `docs/ai-engineering-review.md` đã liệt kê timeout/retry/error taxonomy là runtime gaps. `docs/version-analysis-architecture.md:687-732` giữ telemetry ngoài portable manifest và cấm log full prompt/evidence mặc định.
|
|
220
|
+
|
|
221
|
+
## 3. OpenAI-Compatible Protocol Baseline
|
|
222
|
+
|
|
223
|
+
### 3.1 Chọn Chat Completions làm baseline
|
|
224
|
+
|
|
225
|
+
Baseline cho adapter đầu tiên nên là non-streaming `POST /v1/chat/completions`. OpenAI định nghĩa request bằng `model` và `messages`, trả `choices[].message`, cùng usage token fields; `response_format.type = "json_schema"` bật Structured Outputs. [OpenAI Chat Completions reference](https://developers.openai.com/api/reference/resources/chat/subresources/completions/methods/create) và [Structured Outputs guide](https://developers.openai.com/api/docs/guides/structured-outputs) là baseline tham chiếu, không phải yêu cầu dùng OpenAI service hay SDK.
|
|
226
|
+
|
|
227
|
+
Request tối thiểu do adapter đề xuất:
|
|
228
|
+
|
|
229
|
+
```json
|
|
230
|
+
{
|
|
231
|
+
"model": "<user-selected-model>",
|
|
232
|
+
"messages": [
|
|
233
|
+
{ "role": "system", "content": "<prompt.system>" },
|
|
234
|
+
{ "role": "user", "content": "<prompt.user>" }
|
|
235
|
+
],
|
|
236
|
+
"response_format": {
|
|
237
|
+
"type": "json_schema",
|
|
238
|
+
"json_schema": {
|
|
239
|
+
"name": "upgradelens_version_analysis",
|
|
240
|
+
"strict": true,
|
|
241
|
+
"schema": { "type": "object" }
|
|
242
|
+
}
|
|
243
|
+
},
|
|
244
|
+
"stream": false
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Response tối thiểu cần map:
|
|
249
|
+
|
|
250
|
+
```json
|
|
251
|
+
{
|
|
252
|
+
"model": "<resolved-model>",
|
|
253
|
+
"choices": [
|
|
254
|
+
{
|
|
255
|
+
"message": {
|
|
256
|
+
"role": "assistant",
|
|
257
|
+
"content": "{\"summary\":\"...\"}"
|
|
258
|
+
},
|
|
259
|
+
"finish_reason": "stop"
|
|
260
|
+
}
|
|
261
|
+
],
|
|
262
|
+
"usage": {
|
|
263
|
+
"prompt_tokens": 100,
|
|
264
|
+
"completion_tokens": 25,
|
|
265
|
+
"total_tokens": 125
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Mapping nội bộ:
|
|
271
|
+
|
|
272
|
+
```js
|
|
273
|
+
{
|
|
274
|
+
output: body.choices[0].message.content,
|
|
275
|
+
provider: configuredProviderLabel,
|
|
276
|
+
model: body.model ?? configuredModel,
|
|
277
|
+
usage: {
|
|
278
|
+
inputTokens: body.usage?.prompt_tokens,
|
|
279
|
+
outputTokens: body.usage?.completion_tokens,
|
|
280
|
+
totalTokens: body.usage?.total_tokens
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Authorization baseline là optional `Authorization: Bearer <token>`. OpenAI và OpenRouter bắt buộc Bearer; Ollama local bỏ qua API key; vLLM và LM Studio có thể bật auth; LiteLLM Proxy thường dùng master/virtual key. Adapter không nên giả định auth luôn có hoặc luôn không có.
|
|
286
|
+
|
|
287
|
+
### 3.2 Responses API không thuộc adapter đầu tiên
|
|
288
|
+
|
|
289
|
+
Responses API hiện có ở OpenAI, Ollama, vLLM, LiteLLM, LM Studio và OpenRouter ở các mức khác nhau, nhưng statefulness, tools và structured-output field khác Chat Completions. Ví dụ Ollama chỉ hỗ trợ flavor không stateful; OpenRouter mô tả Responses API là beta/stateless; LM Studio có stateful follow-up. Những khác biệt này không giúp use case hiện tại vốn chỉ gửi một system/user prompt và nhận một JSON candidate.
|
|
290
|
+
|
|
291
|
+
Quyết định LR-00:
|
|
292
|
+
|
|
293
|
+
- implementation đầu tiên chỉ dùng Chat Completions;
|
|
294
|
+
- không thiết kế abstraction chung cho Chat Completions và Responses;
|
|
295
|
+
- chỉ xem xét Responses khi UpgradeLens có requirement cụ thể không thể giải quyết bằng Chat Completions.
|
|
296
|
+
|
|
297
|
+
### 3.3 Compatibility không có nghĩa là mọi capability giống nhau
|
|
298
|
+
|
|
299
|
+
OpenAI-compatible là wire family, không phải chứng nhận mọi model/endpoint hỗ trợ cùng JSON Schema subset, errors hoặc routing. Adapter phải cố định phần chung và giữ khác biệt nhỏ ở configuration/capability:
|
|
300
|
+
|
|
301
|
+
- full endpoint URL;
|
|
302
|
+
- authorization optional;
|
|
303
|
+
- model ID/alias;
|
|
304
|
+
- structured-output mode của endpoint + model;
|
|
305
|
+
- usage mapping defensive;
|
|
306
|
+
- HTTP status/error envelope mapping defensive.
|
|
307
|
+
|
|
308
|
+
Streaming đều có ở các runtime khảo sát nhưng được đặt `false` và ngoài scope.
|
|
309
|
+
|
|
310
|
+
## 4. Runtime Compatibility Matrix
|
|
311
|
+
|
|
312
|
+
Trong cột `Compatibility`, nhãn đánh giá adapter đề xuất, không phải generic HTTP provider hiện tại. Generic provider hiện tại là `REQUIRES_MAPPING` cho **tất cả** runtime trong bảng.
|
|
313
|
+
|
|
314
|
+
| Runtime | Open source / self-hosted | Compatible endpoint | Structured output | Usage metadata | Auth | Compatibility / adapter complexity | Recommended role |
|
|
315
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
316
|
+
| Ollama | Có; MIT; local/self-hosted | `/v1/chat/completions`; `/v1/models`; Responses một phần | `response_format`; JSON mode và JSON Schema; model quality vẫn ảnh hưởng | OpenAI-style usage được endpoint compatibility hỗ trợ | Local không cần; SDK client thường yêu cầu dummy key nhưng server bỏ qua | `DIRECT` / thấp | First local smoke runtime và contributor default |
|
|
317
|
+
| vLLM | Có; Apache-2.0; self-hosted | `/v1/chat/completions`; `/v1/models`; `/v1/responses` | `json_object`, `json_schema`, và structured-output extensions | OpenAI-style usage | Optional `--api-key` | `DIRECT` / thấp; cần model có chat template | High-throughput/self-hosted server validation |
|
|
318
|
+
| LiteLLM Proxy | Có phần core MIT; self-hosted gateway; enterprise folder có license riêng | `/chat/completions` theo quick start; Responses cũng được proxy hỗ trợ | Nhận OpenAI params nhưng support thực tế phụ thuộc upstream model/provider và proxy policy | Chuẩn hóa OpenAI response, usage và có cost/observability riêng | Master key/virtual key Bearer khi cấu hình | `PARTIAL` / thấp ở wire, trung bình ở capability | Optional multi-provider gateway, không phải dependency mặc định |
|
|
319
|
+
| LM Studio | Self-hosted local nhưng desktop app là proprietary, không phải OSS | `/v1/chat/completions`; `/v1/models`; `/v1/responses` | JSON Schema qua `response_format`; docs cảnh báo không phải model nào cũng làm tốt | OpenAI-style usage | Mặc định none; API token Bearer có thể bật | `PARTIAL` / thấp; protocol direct nhưng OSS criterion không đạt | Optional GUI-friendly contributor runtime |
|
|
320
|
+
| OpenRouter | Cloud, không self-hosted | `/api/v1/chat/completions`; `/api/v1/models`; Responses beta | JSON Schema chỉ cho model/provider tương thích; có `require_parameters` để ép routing | OpenAI-like token usage; model-specific accounting | Bearer bắt buộc | `PARTIAL` / thấp-trung bình; cần chọn model/capability | Optional cloud compatibility validation |
|
|
321
|
+
| OpenAI API | Cloud, không self-hosted | `/v1/chat/completions`; `/v1/models`; `/v1/responses` | Native JSON Schema/JSON mode tùy model | Canonical `prompt_tokens`, `completion_tokens`, `total_tokens` | Bearer bắt buộc | `DIRECT` / thấp | Protocol baseline và optional cloud endpoint, không phải default |
|
|
322
|
+
|
|
323
|
+
Nguồn chính thức: [Ollama OpenAI compatibility](https://docs.ollama.com/api/openai-compatibility), [Ollama Structured Outputs](https://docs.ollama.com/capabilities/structured-outputs), [vLLM OpenAI-compatible server](https://docs.vllm.ai/en/stable/serving/openai_compatible_server/), [vLLM Structured Outputs](https://docs.vllm.ai/en/stable/features/structured_outputs/), [LiteLLM docs](https://docs.litellm.ai/), [LM Studio OpenAI compatibility](https://lmstudio.ai/docs/developer/openai-compat), [LM Studio Structured Output](https://lmstudio.ai/docs/developer/openai-compat/structured-output), [OpenRouter API overview](https://openrouter.ai/docs/api/reference/overview), và [OpenRouter Structured Outputs](https://openrouter.ai/docs/guides/features/structured-outputs).
|
|
324
|
+
|
|
325
|
+
License/suitability được đối chiếu từ [Ollama MIT license](https://github.com/ollama/ollama/blob/main/LICENSE), [vLLM Apache-2.0 license](https://github.com/vllm-project/vllm/blob/main/LICENSE), [LiteLLM license](https://github.com/BerriAI/litellm/blob/main/LICENSE), và [LM Studio App Terms](https://lmstudio.ai/app-terms).
|
|
326
|
+
|
|
327
|
+
### 4.1 Mismatch cụ thể
|
|
328
|
+
|
|
329
|
+
**Request:** current provider gửi `{prompt, outputSchema}`; mọi runtime trong bảng cần `model/messages` cho Chat Completions. Current CLI model không nằm trong body. Native structured output cần `response_format`, không phải top-level `outputSchema`.
|
|
330
|
+
|
|
331
|
+
**Response:** current provider đọc `body.output`; Chat Completions trả string tại `body.choices[0].message.content`. Adapter cũng phải từ chối missing/empty choices, refusal, tool-only response và finish reason không chấp nhận được thay vì đẩy `undefined` xuống Ajv.
|
|
332
|
+
|
|
333
|
+
**Structured output:** Ollama/vLLM/LM Studio có native JSON Schema nhưng support cuối cùng vẫn chịu ảnh hưởng inference engine/model và JSON Schema subset. LiteLLM/OpenRouter thêm một lớp routing nên capability là theo selected model/provider, không theo hostname. OpenAI cũng có model cũ chỉ hỗ trợ JSON mode. Vì vậy `provider=openai-compatible` không đồng nghĩa `structuredOutput=jsonSchema` trong mọi cấu hình.
|
|
334
|
+
|
|
335
|
+
**Authentication:** OpenAI/OpenRouter bắt buộc Bearer; local Ollama thường none; vLLM/LM Studio optional; LiteLLM tùy gateway config. Không cần vendor branches—chỉ cần optional header—but không nên tiếp tục yêu cầu user nhập raw header value lâu dài.
|
|
336
|
+
|
|
337
|
+
**Errors:** OpenAI-family errors thường có `error` object, Ollama native docs cũng cho thấy `error` có thể là string, OpenRouter có typed `error.metadata.error_type`, còn upstream gateways có thể thay đổi detail. Adapter nên phân loại trước bằng transport/HTTP status, sau đó đọc bounded, sanitized `error.code/type/message`; không phụ thuộc một envelope duy nhất. [Ollama errors](https://docs.ollama.com/api/errors), [OpenRouter errors](https://openrouter.ai/docs/api/reference/errors-and-debugging), và [OpenAI error codes](https://developers.openai.com/api/docs/guides/error-codes) xác nhận khác biệt này.
|
|
338
|
+
|
|
339
|
+
**Timeout:** không có timeout contract portable trong Chat Completions wire format. Server/gateway có thể trả `408`, `429`, `502`, `503` hoặc đóng transport, nhưng thời gian chờ và retry policy là deployment-specific. Vì vậy client-side deadline của UpgradeLens phải là authority cho tất cả runtime; HTTP/provider signal chỉ giúp phân loại. Không tạo profile timeout theo vendor ở LR-01.
|
|
340
|
+
|
|
341
|
+
### 4.2 Local deployment và hardware ở mức kiến trúc
|
|
342
|
+
|
|
343
|
+
- Ollama là phù hợp nhất với laptop contributor; Apple Silicon dùng Metal native. Model phải vừa unified memory; model size/context quyết định RAM và latency, không phải adapter. [Ollama development/hardware notes](https://github.com/ollama/ollama/blob/main/docs/development.md)
|
|
344
|
+
- LM Studio cũng nhắm local desktop và khuyến nghị 16 GB+ RAM; app hỗ trợ Apple Silicon và local server. [LM Studio system requirements](https://lmstudio.ai/docs/app/system-requirements)
|
|
345
|
+
- vLLM phù hợp hơn cho server throughput và accelerator deployment; hardware/model serving là trách nhiệm operator. Mac contributor không nên phải cài vLLM để dùng UpgradeLens.
|
|
346
|
+
- LiteLLM Proxy không tự cung cấp inference; hardware phụ thuộc upstream. Nó thêm một service/gateway process và configuration.
|
|
347
|
+
- OpenRouter/OpenAI không có local hardware requirement nhưng cần network/account/cost và gửi evidence ra remote service.
|
|
348
|
+
|
|
349
|
+
## 5. OSS-First Architecture Options
|
|
350
|
+
|
|
351
|
+
### Option A — Direct Ollama Adapter
|
|
352
|
+
|
|
353
|
+
```text
|
|
354
|
+
UpgradeLens → OllamaProvider → Ollama
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
Ưu điểm:
|
|
358
|
+
|
|
359
|
+
- đường ngắn nhất để smoke test trên máy hiện tại;
|
|
360
|
+
- native Ollama `/api/chat` hỗ trợ field `format` với JSON Schema;
|
|
361
|
+
- contributor experience tốt và hoàn toàn local.
|
|
362
|
+
|
|
363
|
+
Nhược điểm:
|
|
364
|
+
|
|
365
|
+
- coupling với Ollama request/response native dù Ollama đã cung cấp OpenAI-compatible endpoint;
|
|
366
|
+
- muốn thêm vLLM/LM Studio/cloud sau đó phải thêm adapter song song;
|
|
367
|
+
- auth, errors và usage của native API khác protocol cloud;
|
|
368
|
+
- làm kiến trúc trông Ollama-first thay vì protocol-first.
|
|
369
|
+
|
|
370
|
+
Kết luận: `NOT_RECOMMENDED` làm kiến trúc chính. Chỉ cân nhắc adapter native sau này nếu OpenAI compatibility của Ollama thiếu capability cần thiết; hiện không có bằng chứng cho requirement đó.
|
|
371
|
+
|
|
372
|
+
### Option B — OpenAI-Compatible Adapter
|
|
373
|
+
|
|
374
|
+
```text
|
|
375
|
+
UpgradeLens
|
|
376
|
+
↓
|
|
377
|
+
OpenAiCompatibleProvider
|
|
378
|
+
├── Ollama
|
|
379
|
+
├── vLLM
|
|
380
|
+
├── LM Studio
|
|
381
|
+
├── LiteLLM Proxy
|
|
382
|
+
├── OpenRouter
|
|
383
|
+
└── OpenAI API
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Ưu điểm:
|
|
387
|
+
|
|
388
|
+
- giữ AI core và `AiRuntime` độc lập vendor;
|
|
389
|
+
- một mapper request/response mở khóa cả local và cloud;
|
|
390
|
+
- không cần SDK dependency vì Node đã có `fetch`;
|
|
391
|
+
- đúng với prompt shape hiện tại: `prompt.system` và `prompt.user` map thẳng thành messages;
|
|
392
|
+
- Ollama và vLLM đều có official Chat Completions compatibility và native JSON Schema.
|
|
393
|
+
|
|
394
|
+
Giới hạn thực tế:
|
|
395
|
+
|
|
396
|
+
- endpoint/model capability vẫn cần khai báo nhỏ cho structured output;
|
|
397
|
+
- error envelopes không hoàn toàn đồng nhất;
|
|
398
|
+
- OpenRouter có routing-specific option như `require_parameters` nếu muốn guarantee provider hỗ trợ schema;
|
|
399
|
+
- exact UpgradeLens draft-2020-12 schema cần conformance smoke test trên từng inference engine.
|
|
400
|
+
|
|
401
|
+
Kết luận: `RECOMMENDED`.
|
|
402
|
+
|
|
403
|
+
### Option C — LiteLLM làm gateway chuẩn
|
|
404
|
+
|
|
405
|
+
```text
|
|
406
|
+
UpgradeLens → OpenAiCompatibleProvider → LiteLLM Proxy → providers
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
Ưu điểm:
|
|
410
|
+
|
|
411
|
+
- chuẩn hóa nhiều provider, model alias, keys, errors và usage ở một gateway;
|
|
412
|
+
- có routing, budget, rate limit và observability cho deployment nhiều người;
|
|
413
|
+
- UpgradeLens vẫn chỉ biết OpenAI-compatible protocol.
|
|
414
|
+
|
|
415
|
+
Nhược điểm:
|
|
416
|
+
|
|
417
|
+
- thêm Python/Docker service, proxy config, lifecycle và một failure boundary;
|
|
418
|
+
- local contributor chỉ cần Ollama sẽ phải vận hành thừa một gateway;
|
|
419
|
+
- structured-output support vẫn không thể vượt capability của upstream model/provider;
|
|
420
|
+
- logging/observability của gateway có thể lưu prompt/evidence nếu operator bật callback.
|
|
421
|
+
|
|
422
|
+
Kết luận: LiteLLM nên là **optional gateway**, không phải dependency hoặc default requirement. UpgradeLens phải gọi trực tiếp Ollama/vLLM/LM Studio được bằng cùng adapter.
|
|
423
|
+
|
|
424
|
+
## 6. Recommended Architecture
|
|
425
|
+
|
|
426
|
+
### 6.1 Minimal component shape
|
|
427
|
+
|
|
428
|
+
```text
|
|
429
|
+
analyzeDependencyAiContext
|
|
430
|
+
↓ internal request
|
|
431
|
+
createProviderAiRuntime
|
|
432
|
+
↓ prompt + schema
|
|
433
|
+
OpenAiCompatibleProvider
|
|
434
|
+
↓ HTTP Chat Completions
|
|
435
|
+
configured endpoint/model
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
`AiRuntime` không đổi. Chỉ thêm một provider implementation có trách nhiệm:
|
|
439
|
+
|
|
440
|
+
- map prompt object sang `messages`;
|
|
441
|
+
- map schema sang `response_format` theo declared capability;
|
|
442
|
+
- luôn gửi `stream: false`;
|
|
443
|
+
- thêm model và optional Authorization;
|
|
444
|
+
- bounded fetch + timeout;
|
|
445
|
+
- map Chat Completions response và usage sang internal result;
|
|
446
|
+
- map transport/HTTP/provider errors sang taxonomy nhỏ;
|
|
447
|
+
- không validate business candidate hoặc evidence—đó vẫn là trách nhiệm AI core/trust layer.
|
|
448
|
+
|
|
449
|
+
### 6.2 Endpoint profile: cần object nhỏ, không cần registry
|
|
450
|
+
|
|
451
|
+
Chỉ `baseUrl/apiKey/model` là chưa đủ vì:
|
|
452
|
+
|
|
453
|
+
- code hiện có dùng full endpoint URL, và vendor/gateway có prefix khác nhau (`/v1` so với `/api/v1`);
|
|
454
|
+
- auth có thể none hoặc Bearer;
|
|
455
|
+
- structured output là capability theo endpoint/model;
|
|
456
|
+
- một số runtime trả usage thiếu field.
|
|
457
|
+
|
|
458
|
+
Tuy nhiên không cần named profiles như `ollama`, `vllm`, `openrouter`. Cấu hình construction-time nhỏ sau là đủ:
|
|
459
|
+
|
|
460
|
+
```js
|
|
461
|
+
{
|
|
462
|
+
endpoint: "http://localhost:11434/v1/chat/completions",
|
|
463
|
+
model: "qwen3:latest",
|
|
464
|
+
authorization: null,
|
|
465
|
+
capabilities: {
|
|
466
|
+
structuredOutput: "jsonSchema",
|
|
467
|
+
usage: true,
|
|
468
|
+
streaming: false
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
`endpoint` nên tiếp tục là full URL trong lần đầu để giữ backward compatibility và tránh logic join URL. `authorization` nên được adapter xây từ API key trong API programmatic tương lai; env raw Authorization có thể được giữ làm legacy compatibility. `usage` có thể chỉ điều khiển expectation/telemetry, không làm request fail nếu metadata vắng mặt.
|
|
474
|
+
|
|
475
|
+
Không cần model listing trong runtime path. `/v1/models` hữu ích cho diagnostics/smoke setup nhưng auto-select model sẽ làm behavior khó dự đoán; user phải chọn model rõ ràng.
|
|
476
|
+
|
|
477
|
+
### 6.3 Không negotiation qua probe trong request đầu tiên
|
|
478
|
+
|
|
479
|
+
Capability object nhỏ giải quyết vấn đề thật: cùng wire protocol nhưng structured-output support khác theo model. Nó không nên phát triển thành framework generic.
|
|
480
|
+
|
|
481
|
+
Không nên tự probe endpoint hoặc retry lần lượt ba modes trong mỗi analysis vì:
|
|
482
|
+
|
|
483
|
+
- tốn model calls/cost;
|
|
484
|
+
- một `400` có thể do schema invalid, model missing hoặc prompt too large, không chỉ do capability;
|
|
485
|
+
- fallback âm thầm làm benchmark giữa endpoints không còn tương đương.
|
|
486
|
+
|
|
487
|
+
Capability nên đến từ explicit config/default của validation profile. LR-01 có thể chỉ hỗ trợ `jsonSchema` để mở khóa Ollama; `jsonMode` và `promptOnly` được thêm/validate có chủ đích sau.
|
|
488
|
+
|
|
489
|
+
## 7. Structured Output Strategy
|
|
490
|
+
|
|
491
|
+
### 7.1 Strategy
|
|
492
|
+
|
|
493
|
+
```text
|
|
494
|
+
Native JSON Schema
|
|
495
|
+
↓ only when declared unsupported
|
|
496
|
+
JSON mode
|
|
497
|
+
↓ only when declared unsupported
|
|
498
|
+
Prompt-constrained JSON
|
|
499
|
+
↓
|
|
500
|
+
local Ajv schema validation
|
|
501
|
+
↓
|
|
502
|
+
existing trust validation
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
| Mode | External request | Guarantee trước local validation | Recommendation |
|
|
506
|
+
| --- | --- | --- | --- |
|
|
507
|
+
| `jsonSchema` | `response_format: { type: "json_schema", json_schema: { name, strict, schema } }` | Engine cố ràng buộc schema; exact subset tùy runtime/model | Default cho Ollama smoke, vLLM, supported LM Studio/OpenRouter/OpenAI models |
|
|
508
|
+
| `jsonMode` | `response_format: { type: "json_object" }` | JSON hợp lệ, không guarantee candidate schema | Fallback explicit khi endpoint/model không hỗ trợ JSON Schema |
|
|
509
|
+
| `promptOnly` | Không gửi `response_format`; prompt hiện đã chứa schema và “Return only JSON” | Không có transport-level guarantee | Last-resort compatibility; không nên là silent default |
|
|
510
|
+
|
|
511
|
+
OpenAI docs phân biệt rõ JSON Schema bảo đảm schema adherence còn JSON mode chỉ bảo đảm valid JSON. Ollama docs khuyên vừa truyền schema vừa ghi schema trong prompt; UpgradeLens đã làm phần prompt. vLLM và LM Studio đều document `response_format.type = json_schema`. OpenRouter chỉ guarantee trên model/provider có capability tương ứng.
|
|
512
|
+
|
|
513
|
+
### 7.2 Tool/function calling không phải lựa chọn chính
|
|
514
|
+
|
|
515
|
+
Tool calling có thể ép arguments theo schema nhưng không phù hợp use case này:
|
|
516
|
+
|
|
517
|
+
- UpgradeLens không cần model chọn hay gọi tool;
|
|
518
|
+
- response mapping phức tạp hơn (`tool_calls[].function.arguments`);
|
|
519
|
+
- capability/model template khác nhau nhiều hơn structured response;
|
|
520
|
+
- có thể sinh zero/multiple tool calls và finish reason khác.
|
|
521
|
+
|
|
522
|
+
Chỉ xem xét tool calling nếu một runtime quan trọng không có JSON Schema/JSON mode nhưng có function calling đáng tin cậy. Không có bằng chứng hiện tại cần lựa chọn này.
|
|
523
|
+
|
|
524
|
+
### 7.3 Local validation luôn bắt buộc
|
|
525
|
+
|
|
526
|
+
Mọi mode, kể cả `strict: true`, vẫn phải:
|
|
527
|
+
|
|
528
|
+
1. kiểm tra Chat Completions envelope;
|
|
529
|
+
2. parse `message.content` thành JSON;
|
|
530
|
+
3. validate exact internal `AI_VERSION_ANALYSIS_CANDIDATE_SCHEMA` bằng Ajv;
|
|
531
|
+
4. chạy existing trust validation.
|
|
532
|
+
|
|
533
|
+
Lý do:
|
|
534
|
+
|
|
535
|
+
- runtime có thể hỗ trợ subset khác nhau của JSON Schema;
|
|
536
|
+
- model có thể refuse hoặc dừng vì length;
|
|
537
|
+
- gateway có thể route sang provider không giữ đủ constraint;
|
|
538
|
+
- schema validation không kiểm chứng evidence entailment/trust.
|
|
539
|
+
|
|
540
|
+
### 7.4 Fail-fast và fallback behavior
|
|
541
|
+
|
|
542
|
+
- `jsonSchema` được khai báo nhưng endpoint trả recognized unsupported-parameter/capability error: phát `RUNTIME_UNSUPPORTED_STRUCTURED_OUTPUT`; không publish claims.
|
|
543
|
+
- Không biến mọi `400` thành fallback.
|
|
544
|
+
- Nếu operator đã cấu hình `jsonMode` hoặc `promptOnly`, adapter gửi đúng một request theo mode đó và local validation quyết định pass/fail.
|
|
545
|
+
- Không tự retry bằng mode yếu hơn trong cùng analysis run.
|
|
546
|
+
- Truncated/refusal/tool-only/empty content là `RUNTIME_INVALID_RESPONSE`, không phải schema fallback.
|
|
547
|
+
|
|
548
|
+
### 7.5 Exact schema conformance risk
|
|
549
|
+
|
|
550
|
+
Candidate schema hiện là draft 2020-12 và dùng các keyword như `pattern`, `uniqueItems`, `minItems`, `enum`, `additionalProperties: false`. Official runtime docs xác nhận JSON Schema nói chung nhưng không chứng minh tất cả engine chấp nhận exact schema này. LR-02 phải gửi **exact current schema** trong live smoke.
|
|
551
|
+
|
|
552
|
+
Không nên sửa internal schema để chiều theo provider. Nếu một engine chỉ hỗ trợ subset, một provider-facing schema projection tối thiểu có thể được nghiên cứu sau, trong khi exact internal schema vẫn validate cuối cùng. Projection chưa được đề xuất triển khai ở LR-01 vì smoke evidence chưa cho thấy cần thiết.
|
|
553
|
+
|
|
554
|
+
## 8. Configuration and Security
|
|
555
|
+
|
|
556
|
+
### 8.1 Configuration recommendation
|
|
557
|
+
|
|
558
|
+
Giữ bốn env hiện có cho lần đầu để tránh CLI/config migration:
|
|
559
|
+
|
|
560
|
+
```text
|
|
561
|
+
UPGRADELENS_AI_PROVIDER=openai-compatible
|
|
562
|
+
UPGRADELENS_AI_ENDPOINT=<full chat-completions URL>
|
|
563
|
+
UPGRADELENS_AI_MODEL=<explicit model or gateway alias>
|
|
564
|
+
UPGRADELENS_AI_AUTHORIZATION=<optional full Authorization value>
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
Semantics đề xuất:
|
|
568
|
+
|
|
569
|
+
- `PROVIDER` chọn adapter protocol, không chọn cloud vendor; giá trị mới duy nhất là `openai-compatible`.
|
|
570
|
+
- `ENDPOINT` tiếp tục là full URL.
|
|
571
|
+
- `MODEL` trở thành required cho adapter và thực sự được gửi trong body.
|
|
572
|
+
- `AUTHORIZATION` optional; không log. Giá trị phải gồm scheme trong compatibility phase.
|
|
573
|
+
|
|
574
|
+
Ví dụ không chứa secret:
|
|
575
|
+
|
|
576
|
+
**Ollama local**
|
|
577
|
+
|
|
578
|
+
```text
|
|
579
|
+
UPGRADELENS_AI_PROVIDER=openai-compatible
|
|
580
|
+
UPGRADELENS_AI_ENDPOINT=http://localhost:11434/v1/chat/completions
|
|
581
|
+
UPGRADELENS_AI_MODEL=qwen3:latest
|
|
582
|
+
# UPGRADELENS_AI_AUTHORIZATION unset
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
**vLLM**
|
|
586
|
+
|
|
587
|
+
```text
|
|
588
|
+
UPGRADELENS_AI_PROVIDER=openai-compatible
|
|
589
|
+
UPGRADELENS_AI_ENDPOINT=http://localhost:8000/v1/chat/completions
|
|
590
|
+
UPGRADELENS_AI_MODEL=<served-model-name>
|
|
591
|
+
# UPGRADELENS_AI_AUTHORIZATION=Bearer <runtime-key> # only if server enables auth
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
**LiteLLM Proxy**
|
|
595
|
+
|
|
596
|
+
```text
|
|
597
|
+
UPGRADELENS_AI_PROVIDER=openai-compatible
|
|
598
|
+
UPGRADELENS_AI_ENDPOINT=http://localhost:4000/chat/completions
|
|
599
|
+
UPGRADELENS_AI_MODEL=<gateway-model-alias>
|
|
600
|
+
UPGRADELENS_AI_AUTHORIZATION=Bearer <proxy-key>
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
Future config nên thêm explicit `structuredOutput` mode trong programmatic config hoặc `.upgradelens/config.json`, không nhất thiết thêm env ở LR-01. Nếu sau này có config file, precedence hợp lý là:
|
|
604
|
+
|
|
605
|
+
```text
|
|
606
|
+
injected AiRuntime / explicit API options
|
|
607
|
+
↓
|
|
608
|
+
environment variables
|
|
609
|
+
↓
|
|
610
|
+
.upgradelens/config.json non-secret fields
|
|
611
|
+
↓
|
|
612
|
+
defaults
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
Config file chỉ nên chứa endpoint, provider, model và capability. API key/Authorization không được lưu raw trong repository config; secret chỉ đến từ env hoặc external secret injection. Task này không triển khai config file.
|
|
616
|
+
|
|
617
|
+
Về lâu dài, `UPGRADELENS_AI_API_KEY` an toàn về UX hơn raw `AUTHORIZATION` vì adapter tự thêm `Bearer` và dễ redact. Nếu bổ sung, `AUTHORIZATION` legacy nên thắng `API_KEY` khi cả hai được set hoặc tốt hơn là fail configuration do ambiguity; không cần quyết định/migrate trong LR-01.
|
|
618
|
+
|
|
619
|
+
### 8.2 Security review
|
|
620
|
+
|
|
621
|
+
| Risk | Hiện tại | Guardrail tối thiểu đề xuất |
|
|
622
|
+
| --- | --- | --- |
|
|
623
|
+
| API key bị log | Provider không chủ động log headers; CLI chỉ log error message. Upstream gateway có thể log. | Không đưa headers/config vào error, telemetry hoặc snapshot; redact `authorization`, `api-key`, query credentials. |
|
|
624
|
+
| Authorization trong error | Current non-2xx error không chứa headers/body nên không leak; transport error từ fetch đi qua có thể khác nhau. | Tạo package-local sanitized errors; không append request options/raw provider body. |
|
|
625
|
+
| Secret trong endpoint URL | Current code chấp nhận userinfo/query và có thể để URL xuất hiện ở external logs. | Reject URL có username/password; khuyến cáo không dùng key trong query; sanitize query khi hiển thị. |
|
|
626
|
+
| Prompt/evidence logging | UpgradeLens runtime hiện không log prompt; remote endpoint vẫn nhận toàn bộ evidence. LiteLLM/LM Studio diagnostics có thể log input/output khi bật. | Chỉ log run/context IDs, sizes, provider label/model, latency và category; full prompt/body phải opt-in, redacted và không vào portable artifact. |
|
|
627
|
+
| Local HTTP | Hợp lý với loopback Ollama/vLLM/LM Studio. | Cho phép plain HTTP chỉ với loopback (`localhost`, `127.0.0.0/8`, `[::1]`) mặc định. |
|
|
628
|
+
| Remote HTTP | Prompt/evidence và key có thể bị nghe lén. | Require HTTPS cho non-loopback; explicit insecure override chỉ nếu có use case self-hosted private network sau này. |
|
|
629
|
+
| Response size | Current `response.text()` unbounded. | Giới hạn nhỏ, ví dụ 1 MiB cho non-streaming candidate/error body; cancel body khi vượt giới hạn. |
|
|
630
|
+
| Timeout | Không có. | Một AbortController deadline, mặc định khoảng 60–120 giây cho local LLM và configurable bounded maximum; taxonomy riêng cho timeout. |
|
|
631
|
+
| Redirect | Fetch mặc định follow; có thể chuyển prompt/header sang destination ngoài ý muốn tùy fetch policy. | `redirect: "error"`; user cấu hình endpoint cuối cùng rõ ràng. |
|
|
632
|
+
| SSRF | Endpoint do user cấu hình có thể POST evidence tới internal service/metadata host. Đây là local CLI explicit configuration nên risk thấp hơn server multi-tenant nhưng vẫn tồn tại. | Chỉ `http:`/`https:`; reject URL credentials; remote HTTPS; document trusted-user config. Nếu sau này endpoint đến từ untrusted repo config, block link-local/metadata/private targets hoặc require confirmation. |
|
|
633
|
+
| Prompt injection | Evidence là untrusted input, nhưng đây không phải adapter concern. | Không cho evidence điều khiển endpoint/headers; giữ prompt guardrails, schema validation và trust validation; thêm adversarial eval riêng. |
|
|
634
|
+
|
|
635
|
+
Error body chỉ nên đọc trong cùng response byte limit. Human-readable CLI có thể dùng sanitized `error.message`, nhưng không nên in raw provider metadata/body vì nó có thể echo prompt hoặc secret.
|
|
636
|
+
|
|
637
|
+
### 8.3 Retry policy
|
|
638
|
+
|
|
639
|
+
LR-01 không cần retry để mở khóa model thật. Nếu thêm sau:
|
|
640
|
+
|
|
641
|
+
- tối đa một bounded retry;
|
|
642
|
+
- chỉ transport reset/unreachable có dấu hiệu transient, `429`, hoặc `503` và chỉ khi deadline còn đủ;
|
|
643
|
+
- tôn trọng bounded `Retry-After`;
|
|
644
|
+
- không retry auth, model-not-found, invalid request/schema, response parse/schema/trust failures;
|
|
645
|
+
- local long-running timeout không tự retry mặc định vì có thể nhân đôi inference load.
|
|
646
|
+
|
|
647
|
+
OpenRouter chính thức document `Retry-After` cho `429/503`; logic này có thể áp dụng protocol-level mà không tạo OpenRouter branch.
|
|
648
|
+
|
|
649
|
+
## 9. Error Taxonomy
|
|
650
|
+
|
|
651
|
+
Đề xuất một package-local `AiRuntimeError` với stable `code`, sanitized message, optional HTTP status và `retryable`. Không đưa raw body/header/prompt vào error.
|
|
652
|
+
|
|
653
|
+
| Code | Khi nào | Retryable | Configuration/fatal | CLI message gợi ý |
|
|
654
|
+
| --- | --- | --- | --- | --- |
|
|
655
|
+
| `RUNTIME_NOT_CONFIGURED` | Thiếu endpoint, model hoặc adapter selection | Không | Fatal config | “AI runtime is not configured; set provider, endpoint and model.” |
|
|
656
|
+
| `RUNTIME_UNREACHABLE` | DNS/refused/reset/transport không tới endpoint | Có thể, tối đa một lần | Không nhất thiết | “Cannot reach the configured AI endpoint.” |
|
|
657
|
+
| `RUNTIME_TIMEOUT` | Abort deadline khi connect/read/inference | Thường không cho local; optional một retry policy | Không | “AI request timed out after <bounded duration>.” |
|
|
658
|
+
| `RUNTIME_AUTH_FAILED` | HTTP 401/403 với auth semantics | Không | Fatal config/permission | “AI endpoint rejected authentication or authorization.” |
|
|
659
|
+
| `RUNTIME_RATE_LIMITED` | HTTP 429 hoặc stable provider type | Có, một lần theo bounded Retry-After | Không | “AI endpoint rate limited the request.” |
|
|
660
|
+
| `RUNTIME_MODEL_NOT_FOUND` | HTTP 404/stable error cho configured model | Không | Fatal config | “Configured AI model is unavailable at this endpoint.” |
|
|
661
|
+
| `RUNTIME_UNSUPPORTED_STRUCTURED_OUTPUT` | Endpoint/model từ chối requested `response_format` | Không; user chọn weaker mode rõ ràng | Fatal capability config | “Model/endpoint does not support configured structured-output mode.” |
|
|
662
|
+
| `RUNTIME_INVALID_RESPONSE` | Non-JSON envelope, missing choice/content, refusal/tool-only unexpected, oversized/truncated response | Không | Runtime/model contract failure | “AI endpoint returned an invalid Chat Completions response.” |
|
|
663
|
+
| `RUNTIME_PROVIDER_ERROR` | Các 4xx/5xx/upstream error còn lại | Chỉ 502/503 có thể retry một lần | Tùy status | “AI endpoint returned provider error <status/category>.” |
|
|
664
|
+
|
|
665
|
+
`OUTPUT_JSON_INVALID` và `OUTPUT_SCHEMA_INVALID` nên vẫn là analysis/output validation errors sau khi adapter đã trả một valid Chat Completions envelope. Chúng không nên đại diện transport/provider errors nữa.
|
|
666
|
+
|
|
667
|
+
Mapping ưu tiên:
|
|
668
|
+
|
|
669
|
+
1. local configuration validation;
|
|
670
|
+
2. timeout/transport;
|
|
671
|
+
3. HTTP status (`401/403`, `404`, `429`, `5xx`);
|
|
672
|
+
4. bounded stable provider code/type khi có;
|
|
673
|
+
5. response envelope/content;
|
|
674
|
+
6. candidate JSON/schema trong AI core.
|
|
675
|
+
|
|
676
|
+
Không cần retry engine, nested causes portable hay provider-specific subclasses.
|
|
677
|
+
|
|
678
|
+
## 10. Recommended First Runtime
|
|
679
|
+
|
|
680
|
+
Chọn **Ollama local qua `/v1/chat/completions`**.
|
|
681
|
+
|
|
682
|
+
Lý do theo tiêu chí:
|
|
683
|
+
|
|
684
|
+
1. Ollama core là open source MIT và self-hosted.
|
|
685
|
+
2. Máy test đã cài Ollama và có `qwen3:latest`, `llama3:latest`.
|
|
686
|
+
3. Không cần account, network inference hay API key.
|
|
687
|
+
4. Cùng request/response mapper sau đó dùng được cho vLLM, LM Studio, LiteLLM, OpenRouter và OpenAI.
|
|
688
|
+
5. Official docs xác nhận JSON mode, `response_format`, streaming và OpenAI-compatible Chat Completions; native structured-output docs xác nhận JSON Schema.
|
|
689
|
+
6. Apple Silicon dùng local Metal path; 16 GB phù hợp smoke với model/quantization vừa bộ nhớ, dù throughput và quality không được đảm bảo.
|
|
690
|
+
|
|
691
|
+
Không chọn vLLM đầu tiên vì setup server/hardware phức tạp hơn laptop contributor. Không chọn LiteLLM vì nó thêm gateway mà không cần cho một local Ollama endpoint. Không chọn LM Studio vì app không phải OSS và Ollama đã cài. Không chọn OpenRouter/OpenAI vì cần cloud account/key, gửi evidence ra ngoài và có thể phát sinh chi phí.
|
|
692
|
+
|
|
693
|
+
### Technical smoke vs quality benchmark
|
|
694
|
+
|
|
695
|
+
**Smoke validation kỹ thuật** chỉ pass khi:
|
|
696
|
+
|
|
697
|
+
- adapter kết nối Ollama;
|
|
698
|
+
- exact model được chọn;
|
|
699
|
+
- exact candidate schema được gửi;
|
|
700
|
+
- response envelope/usage được map;
|
|
701
|
+
- candidate parse + Ajv pass;
|
|
702
|
+
- trust validation chạy và artifact schema hợp lệ;
|
|
703
|
+
- invalid model/schema/auth-like/error cases được phân loại đúng;
|
|
704
|
+
- không leak prompt/secret trong logs/errors.
|
|
705
|
+
|
|
706
|
+
**Quality benchmark** là task khác: chạy golden dataset đủ lớn, so risk/evidence/human-review quality, latency và stability giữa model/prompt. Việc một call `qwen3:latest` hoặc `llama3:latest` pass schema không chứng minh model đủ tốt cho production. Model tag `latest` cũng không ổn định cho benchmark reproducibility; quality run nên pin model digest/version nếu Ollama cho phép.
|
|
707
|
+
|
|
708
|
+
## 11. Minimal Implementation Plan
|
|
709
|
+
|
|
710
|
+
Tối đa ba task, theo thứ tự mở khóa nhỏ nhất.
|
|
711
|
+
|
|
712
|
+
### LR-01 — OpenAI-Compatible Chat Completions Provider
|
|
713
|
+
|
|
714
|
+
**Mục tiêu:** thêm một provider dùng `fetch` để map contract hiện tại sang non-streaming OpenAI-compatible Chat Completions với native JSON Schema.
|
|
715
|
+
|
|
716
|
+
**Phạm vi:** request/response/usage mapping, required endpoint+model, optional Authorization, timeout, response size, sanitized error taxonomy; wire vào `provider=openai-compatible` nhưng giữ generic HTTP provider nếu backward compatibility cần.
|
|
717
|
+
|
|
718
|
+
**Files/module có thể đổi:** `src/ai-runtime.js` hoặc module provider nhỏ mới; `src/cli.js`; exports; runtime/CLI unit tests; runtime configuration docs. Không sửa prompt, candidate schema hoặc trust layer.
|
|
719
|
+
|
|
720
|
+
**Test strategy:** fake fetch cho exact headers/body; no-auth/Bearer; choices/content/model/usage mapping; 401/404/429/5xx; transport; timeout; oversized/non-JSON/missing-content; assert error không chứa auth/prompt; no live network in default tests.
|
|
721
|
+
|
|
722
|
+
**Acceptance criteria:** exact current `AiRuntime` request đi qua adapter; `model/messages/response_format/stream:false` đúng; internal usage normalized; stable errors; existing fake/generic tests pass; không thêm SDK/dependency.
|
|
723
|
+
|
|
724
|
+
**Out of scope:** live model, streaming, Responses API, auto model listing/selection, retry engine, JSON-mode fallback, config file.
|
|
725
|
+
|
|
726
|
+
### LR-02 — Ollama Local Smoke Validation
|
|
727
|
+
|
|
728
|
+
**Mục tiêu:** chứng minh LR-01 hoạt động trên máy contributor với real Ollama và exact UpgradeLens candidate schema.
|
|
729
|
+
|
|
730
|
+
**Phạm vi:** manual/opt-in smoke command hoặc test, `qwen3:latest` trước và `llama3:latest` làm secondary nếu cần; capture sanitized evidence về envelope/schema/trust/latency/usage.
|
|
731
|
+
|
|
732
|
+
**Files/module có thể đổi:** live-validation docs; optional opt-in smoke fixture/script theo convention repository; không đổi production behavior trừ bug nhỏ phát hiện từ conformance test và được review riêng.
|
|
733
|
+
|
|
734
|
+
**Test strategy:** không chạy trong default CI; local endpoint only; một bounded request per selected case; test model-not-found và unsupported-schema bằng controlled input nếu không gọi thêm model.
|
|
735
|
+
|
|
736
|
+
**Acceptance criteria:** một real-evidence dependency tạo candidate schema-valid và trust-validated artifact, hoặc failure được phân loại đúng; exact schema compatibility được ghi nhận; không claim quality production.
|
|
737
|
+
|
|
738
|
+
**Out of scope:** quality score, model recommendation production, cloud, paid calls, performance tuning.
|
|
739
|
+
|
|
740
|
+
### LR-03 — Gateway/Cloud Compatibility Validation
|
|
741
|
+
|
|
742
|
+
**Mục tiêu:** kiểm chứng cùng adapter trên ít nhất vLLM hoặc LiteLLM Proxy, rồi một optional cloud endpoint (OpenRouter hoặc OpenAI) do maintainer cung cấp ngoài repository.
|
|
743
|
+
|
|
744
|
+
**Phạm vi:** conformance matrix tests cho endpoint/model capability; quyết định có cần explicit `jsonMode/promptOnly` config và OpenRouter `require_parameters` extension hay không.
|
|
745
|
+
|
|
746
|
+
**Files/module có thể đổi:** provider conformance tests, config docs, optional capability field; không thêm gateway dependency vào UpgradeLens.
|
|
747
|
+
|
|
748
|
+
**Test strategy:** recorded/fake contract tests mặc định; opt-in live validation với secret qua env; assert no secret in artifacts/logs; compare usage/error mapping.
|
|
749
|
+
|
|
750
|
+
**Acceptance criteria:** ít nhất một OSS server ngoài Ollama dùng cùng adapter; một gateway/cloud path được document hoặc deferred với evidence; mọi fallback explicit và local validation vẫn bắt buộc.
|
|
751
|
+
|
|
752
|
+
**Out of scope:** provider registry, dynamic plugins, mandatory LiteLLM, multi-provider routing, streaming, secret manager.
|
|
753
|
+
|
|
754
|
+
## 12. Acceptance Criteria for Live Smoke Validation
|
|
755
|
+
|
|
756
|
+
LR-02 chỉ được coi là pass khi tất cả điều kiện sau có evidence:
|
|
757
|
+
|
|
758
|
+
- [ ] Ollama đang chạy trên loopback; endpoint là `/v1/chat/completions` và không dùng cloud Ollama.
|
|
759
|
+
- [ ] Model được chọn rõ ràng từ model đã cài; model ID thực sự xuất hiện trong request.
|
|
760
|
+
- [ ] Không set hoặc ghi API key thật; Authorization absent cho local Ollama.
|
|
761
|
+
- [ ] Request có đúng hai messages từ `prompt.system`/`prompt.user`, `stream: false`, và native `response_format.json_schema` chứa exact current candidate schema.
|
|
762
|
+
- [ ] Request có bounded timeout; response/error body có byte limit; redirect không được follow.
|
|
763
|
+
- [ ] Response là Chat Completions envelope hợp lệ với đúng một usable assistant content; model/usage được map nếu có.
|
|
764
|
+
- [ ] Assistant content parse thành JSON và pass exact Ajv candidate schema.
|
|
765
|
+
- [ ] Existing trust validation chạy; invented/invalid evidence refs không xuất hiện trong final claims.
|
|
766
|
+
- [ ] Version Analysis artifact pass schema/invariants và không chứa execution secrets/full prompt.
|
|
767
|
+
- [ ] Model-not-found hoặc một controlled runtime failure tạo đúng stable category và human-readable CLI message, không bị gọi là `OUTPUT_SCHEMA_INVALID`.
|
|
768
|
+
- [ ] Error/log output không chứa Authorization, full prompt/evidence hoặc raw provider response.
|
|
769
|
+
- [ ] Smoke result ghi model tag/version, Ollama version, latency, usage availability và exact schema result.
|
|
770
|
+
- [ ] Tài liệu ghi rõ đây là transport/schema smoke, không phải quality benchmark.
|
|
771
|
+
- [ ] Không có live test trong default CI và không có paid model call.
|
|
772
|
+
|
|
773
|
+
## 13. Risks and Open Questions
|
|
774
|
+
|
|
775
|
+
1. **Exact JSON Schema subset:** Ollama, vLLM, LM Studio và cloud providers có chấp nhận `pattern`, `uniqueItems`, `$schema` và toàn bộ draft-2020-12 candidate schema không? Official docs chưa đủ để kết luận; LR-02 phải trả lời bằng exact-schema smoke.
|
|
776
|
+
2. **Reasoning model content:** một số local reasoning models có thể đặt reasoning ở field riêng hoặc trộn marker vào content. Adapter đầu tiên chỉ nhận `message.content` là JSON; có cần disable thinking bằng runtime-specific field sẽ chỉ được quyết định sau smoke. Không thêm vendor field trước khi có evidence.
|
|
777
|
+
3. **Ollama `latest` reproducibility:** local tag có thể đổi. Smoke dùng được, benchmark nên pin digest/version.
|
|
778
|
+
4. **Capability source:** LR-01 nên hard-code `jsonSchema` cho adapter path hay expose construction-time field ngay? Khuyến nghị expose field programmatic nhỏ nhưng CLI lần đầu có thể default `jsonSchema`; không cần env mới trước LR-03.
|
|
779
|
+
5. **Schema refusal/truncation:** cần quyết định accepted `finish_reason`; tối thiểu chỉ `stop` với non-empty content. `length`, refusal hoặc tool-only nên invalid response.
|
|
780
|
+
6. **Error detail portability:** HTTP status mapping ổn định hơn provider `error.code`. Chỉ dựa vào known code để tinh chỉnh, luôn có fallback `RUNTIME_PROVIDER_ERROR`.
|
|
781
|
+
7. **Timeout default:** local 7B/8B trên 16 GB có thể cold-start lâu hơn cloud. Cần đo smoke để chọn default giữa 60–120 giây và bounded maximum hợp lý; không để unbounded.
|
|
782
|
+
8. **Remote privacy:** evidence có thể là public registry facts hiện tại nhưng kiến trúc tương lai có thể chứa dữ liệu repository nhạy cảm. Cloud endpoint phải là explicit user choice và docs phải nói rõ dữ liệu rời máy.
|
|
783
|
+
9. **Config file trust:** `.upgradelens/config.json` trong repo có thể biến endpoint thành untrusted input/SSRF. Nếu triển khai sau, không tự động gửi prompt tới repo-provided remote endpoint mà không có trust/confirmation policy.
|
|
784
|
+
10. **Usage optionality:** một endpoint có thể thiếu hoặc ước lượng usage khác nhau. Thiếu usage không nên làm analysis fail; benchmark phải biểu diễn `null`, không fabricates zero.
|
|
785
|
+
11. **LiteLLM/OpenRouter routing:** capability theo downstream model/provider. Nếu cần guarantee, có thể thêm explicit extra request fields sau conformance evidence; không tạo vendor profile ngay.
|
|
786
|
+
12. **LM Studio role:** protocol phù hợp và UX tốt, nhưng app proprietary nên không thể là OSS-first default. Nó vẫn là endpoint optional hợp lệ.
|
|
787
|
+
13. **Semantic grounding:** JSON Schema và trust ref allowlist không chứng minh claim được evidence entail. Đây là quality/trust backlog, không phải lý do mở rộng adapter.
|
|
788
|
+
|
|
789
|
+
Quyết định cuối cùng của LR-00: triển khai một Chat Completions provider nhỏ, protocol-first; smoke đầu tiên với Ollama local; giữ LiteLLM optional; luôn validate exact internal schema và trust layer; không thêm SDK, plugin framework, Responses API hoặc streaming ở vòng đầu.
|