Feat hzw 20260810 - #3920
Feat hzw 20260810#3920
Conversation
…erence params) + integrate develop features (allow_chat_metadata/vlm4/useModelList/dreaming/etc)
- ModelAddDialogV2: add model prop for edit mode (prefill custom form from existing model, default to 自定义接入 tab, call updateSingleModel/updateManageTenantModel on submit instead of add) - modelConfig.tsx: handleCardEdit now renders ModelAddDialogV2 with model prop instead of ModelEditDialogV2
…e inference fixes Capacity suggestion (catalog -> bundled LiteLLM JSON -> default): - model_capacity_suggestion_service: remove LLM self-report fallback (dead code, providers without web search always return null); add _litellm_lookup reading the bundled LiteLLM model_prices_and_context_window.json (3818 models) as the second source; match by provider/name and bare-name final segment, preferring entries with both max_input and max_output - main Dockerfile: bundle LiteLLM JSON at build time (works offline / no VPN) - suggest_capacity: try LiteLLM bare-name match even when provider is uninferable (base_url still empty while typing) Model add/edit dialog (V2): - custom tab: debounced auto-lookup on model name (500ms) fills empty capacity fields only; configured tag reflects lookup result, not display_name presence - connectivity probe no longer mutates capacity fields; probe carries temperature/top_p/extra_params so invalid __custom__ params surface at verify time as a 400 instead of failing at runtime - __custom__ numeric strings coerce to numbers (top_k=50, not 50); fix .trim crash on numeric values in edit mode - batch tab: add client-side model name search filter; per-row connectivity fills capacity from suggestion - edit mode: onConnectivityChange reports probe result back to the model list so connect_status refreshes in place Model config list: - verifyModels now probes ALL models in the list (was: default-model selection only), updating rows in parallel - remove misleading provider model-count badge; fix ModelConnectStatus duplicate declaration; drop unused DEFAULT_* imports Type inference (_infer_model_type_from_name): - match full name AND final path segment so repo-prefixed ids from aggregators (BAAI/bge-m3, Pro/..., deepseek-ai/...) classify correctly - add contains-based rules aligned with develop TokenPony classifier: embedding/rerank/stt/tts mid-name, vlm3 (omni/video), vlm2 (image-gen keywords), vlm (vision/visual/ocr/vl-segment) Connectivity service: - port develop _embedding_url_candidates multi-candidate probe (normalized /embeddings URL first, then as-given) for embedding/multi_embedding - _config_to_context: __custom__ KV pairs flow into extra_body at runtime - openai_llm check_connectivity probe carries inference params Type fixes: AgentDraft includes model_params_override; ModelEditDialogV2 keyMap adds vlm4; agent-prompt model override config dialog typing
# Conflicts: # backend/consts/const.py # backend/services/model_health_service.py # frontend/public/locales/en/common.json # frontend/public/locales/zh/common.json
…odelList API rename)
The batch-import connectivity probe passed the bare provider root (e.g. https://api.siliconflow.cn/v1/) straight to the rerank adapter, which POSTs the URL as-is -> 404 -> unavailable even though the model is fine. prepare_model_dict already appends /rerank when SAVING the model, so probe-time and save-time URLs disagreed. Mirror that munging in _perform_connectivity_check: dashscope roots get the api/v1 .../services/rerank/text-rerank/text-rerank path, others get {root}/rerank. Already-normalized URLs pass through untouched. This matches the embedding probe /embeddings normalization.
Batch submit requires every enabled row to pass the connectivity probe (hasUnchecked gate), but the verified available result lived only in dialog state — the create endpoints never received it, so the backend reset connect_status to not_detected on insert and the freshly imported models showed as unverified in the list. The backend already honors this (create_model_for_tenant: connect_status = payload or NOT_DETECTED; ModelRequest has the field) — only the frontend was not sending it. Thread connectStatus through addCustomModel / createManageTenantModel request bodies and set it from the row state at batch submit time.
The button opened a 2000-line legacy panel whose single-row edit/delete duplicated the per-row actions (and used the old V1 edit dialog), and whose only unique capability was a narrow bulk-edit (same-provider key, timeout, capacity override). Bulk delete did not exist - deletion inside the panel was still one-by-one. - drop the top-bar button (Can model:update wrapper) - drop the capacity-coverage alert action that opened the same panel - remove isDeleteModalOpen state, ModelDeleteDialog import and instance - delete the orphaned ModelDeleteDialog.tsx (ModelEditDialog stays - it is still referenced by resource-manage ModelList)
The type column rendered t(`model.type.${type}`) directly, but the locale
files have no model.type.vlm2/vlm3/vlm4 keys (they are keyed by semantic
name: imageGeneration / videoUnderstanding / audioUnderstanding), so those
rows displayed the raw id string. Add the same id-to-semantic-key mapping
the add dialog uses, covering all ten types.
Also aligns vlm to the dialog label (image understanding) instead of the
legacy model.type.vlm wording.
…points
SSRF (critical) in openai_provider.get_models: the operator-supplied
base_url was fetched as-is. Add _validate_provider_base_url guard before
the request: scheme must be http/https, host required, private / link-local
/ multicast / reserved IP literals rejected (cloud metadata endpoints,
internal routers). Plain DNS names and the documented localhost/127.0.0.1
local-LLM exemption are allowed. Validation errors flow through the
existing _classify_provider_error path as provider fetch failures.
Information exposure (medium x5) in model_managment_app catalog endpoints:
the exception text was interpolated into the JSON response body, which can
leak stack traces / internal details to the caller. Replace with fixed
messages; the exception detail is already logged server-side via
logger.warning. Affected: /catalog/all, /catalog/providers,
/catalog/inference_field_specs, /catalog/providers/{provider}/models,
/catalog/providers/{provider}/models/{model_name}.
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved critical and moderate findings affect runtime behavior, API contracts, tests, security, and deployment.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
This pull request expands model configuration across the backend, SDK, frontend, persistence, catalog, and deployment layers.
Changes:
- Adds model catalogs, provider discovery, and capacity suggestions.
- Adds inference parameters and agent/knowledge-base overrides.
- Updates model UI, connectivity checks, migrations, tests, and deployment wiring.
Unresolved findings include catalog mismatches, provider and TLS issues, dropped overrides, exception/configuration problems, an invalid test, an incorrect frontend port, and an unpinned catalog dependency.
File summaries
| File | Reviewed change |
|---|---|
test/backend/test_model_consts.py |
Tests model inference-parameter schemas and defaults. |
test/backend/configs/test_model_catalog_loader.py |
Tests catalog loading and defaults. |
test/backend/configs/__init__.py |
Initializes the backend test package. |
sdk/nexent/core/models/openai_llm.py |
Adds inference parameters to connectivity checks. |
frontend/types/modelConfig.ts |
Defines model, catalog, and inference types. |
frontend/types/agentConfig.ts |
Defines agent override types. |
frontend/stores/agentStore.ts |
Tracks agent draft model overrides. |
frontend/stores/agentConfigStore.ts |
Tracks editable agent configuration state. |
frontend/services/modelService.ts |
Handles model, catalog, batch, and inference API operations. |
frontend/services/api.ts |
Provides frontend API helpers. |
frontend/services/agentConfigService.ts |
Maps agent configuration API data. |
frontend/server.js |
Configures the production frontend server. |
frontend/hooks/model/useModelCatalog.ts |
Loads model catalog data. |
frontend/hooks/model/useInferenceFieldSpecs.ts |
Loads inference field specifications. |
frontend/app/[locale]/newchat/assistant-ui/thread-list.tsx |
Adds batch conversation selection and deletion. |
frontend/app/[locale]/models/components/model/ModelOverrideModal.tsx |
Provides model override editing. |
frontend/app/[locale]/models/components/model/ModelListCard.tsx |
Renders model list cards. |
frontend/app/[locale]/models/components/model/ModelItemCard.tsx |
Renders individual model cards. |
frontend/app/[locale]/models/components/model/ModelEditDialogV2.tsx |
Edits model configuration and inference defaults. |
frontend/app/[locale]/models/components/model/ModelCapacityFields.tsx |
Renders model capacity settings. |
frontend/app/[locale]/models/components/model/ModelAdvancedSettings.tsx |
Renders advanced inference settings. |
frontend/app/[locale]/models/components/model/DefaultModelDialog.tsx |
Configures default models. |
frontend/app/[locale]/knowledges/KnowledgeBaseConfiguration.tsx |
Adds knowledge-base embedding overrides. |
frontend/app/[locale]/knowledges/components/document/DocumentList.tsx |
Provides knowledge-base document controls. |
frontend/app/[locale]/agents/components/agent-prompt.tsx |
Adds per-model agent overrides. |
deploy/sql/migrations/v2.6.0_0806_add_model_inference_params.sql |
Persists model inference parameters. |
deploy/images/dockerfiles/main/Dockerfile |
Bundles the runtime model catalog. |
backend/services/providers/openai_provider.py |
Discovers models from OpenAI-compatible providers. |
backend/services/model_provider_service.py |
Coordinates provider discovery. |
backend/services/model_management_service.py |
Builds provider model lists. |
backend/services/model_health_service.py |
Handles model health and connectivity checks. |
backend/services/model_gateway_service.py |
Maps inference parameters into gateway requests. |
backend/services/model_capacity_suggestion_service.py |
Provides catalog and LiteLLM capacity suggestions. |
backend/management/services/agent/service.py |
Persists agent override updates. |
backend/management/services/agent/management.py |
Manages agent configuration operations. |
backend/database/model_management_db.py |
Filters and persists model parameters. |
backend/database/db_models.py |
Defines model and agent database fields. |
backend/database/agent_db.py |
Persists agent model overrides. |
backend/consts/model.py |
Defines model catalog and inference schemas. |
backend/consts/const.py |
Defines shared configuration constants. |
backend/configs/model_catalog.json |
Provides preset provider and model metadata. |
backend/configs/model_catalog_loader.py |
Loads and normalizes the model catalog. |
backend/configs/__init__.py |
Initializes the backend configuration package. |
backend/apps/model_managment_app.py |
Exposes catalog and management endpoints. |
backend/agents/create_agent_info.py |
Applies runtime agent model overrides. |
Review details
Suppressed comments (13)
backend/services/model_gateway_service.py:95
- This helper drops every fixed inference field and returns only
extra_params.__custom__.enable_thinkingis explicitly stored inextra_paramsand the model/agent UI sends it there, so gateway adapters silently ignore that setting (including connectivity probes) even though the model record persists it. Merge the supported fixed fields into the adapter request as well, with provider-specific translation where required.
custom = extra_params.get("__custom__")
return custom if isinstance(custom, dict) and custom else None
backend/services/providers/openai_provider.py:50
- When the request supplies
model_type, the caller skips name inference, but this provider intentionally leavesmodel_typeoff every row. Typed discovery therefore returns rows without their requested type;ModelAddDialogV2falls back tollm, so embedding/rerank/voice requests can be presented and saved as LLMs. Preserve the requested type in the annotation when it is supplied.
try:
ip = ipaddress.ip_address(host)
deploy/images/dockerfiles/main/Dockerfile:78
- Fetching this runtime dependency from
@mainmakes otherwise identical image builds consume different catalog contents over time. Worse, a transient build-network failure silently writes{}, producing an image with no LiteLLM capacity fallback. Pin a release/commit or vendor a known artifact, and make an unavailable catalog visible rather than silently changing behavior.
https://cdn.jsdelivr.net/gh/BerriAI/litellm@main/model_prices_and_context_window.json \
|| echo '{}' > /opt/nexent/litellm_models.json
deploy/sql/migrations/v2.6.0_0806_add_model_inference_params.sql:42
- The frontend now exposes and edits per-knowledge-base embedding overrides, but this migration adds storage only to
ag_tenant_agent_t;createKnowledgeBasesends no override field and the knowledge record has no corresponding column. Values changed in the new KB UI are therefore discarded when the KB is created. Add a KB persistence/API path or remove the UI until it is supported.
ALTER TABLE nexent.ag_tenant_agent_t
ADD COLUMN IF NOT EXISTS model_params_override JSONB DEFAULT NULL;
frontend/app/[locale]/agents/components/agent-prompt.tsx:149
- This effect only depends on
configuringModelId, so it can initialize whileuseInferenceFieldSpecsis still empty and never rerun when the specs request resolves. Opening the override modal during that race leaves the form without the model defaults/spec fields; include the loaded model/specs and the relevant override entry in the initialization dependencies.
}, [configuringModelId]);
frontend/app/[locale]/models/components/model/ModelAddDialogV2.tsx:924
- On the custom add path,
buildInferenceParamsPayloadreturns voice fields as snake_case (model_factory,model_appid,access_token), butaddCustomModelserializes only the camelCasemodelFactory/modelAppid/accessTokenproperties. Unlike the batch path, these fields are never mapped, so STT/TTS models are created with the default OpenAI-compatible factory and lose required credentials. Add the camelCase voice fields before submitting.
...inferencePayload,
frontend/app/[locale]/models/components/model/ModelAddDialogV2.tsx:920
- The custom-submit path never copies the successful
customConnectivityresult intomodelParams, unlike the batch path. Consequently a model that just passed the connectivity probe is created withoutconnect_statusand is stored asnot_detecteddespite the new API field intended to persist the verified status.
const modelParams: any = {
name: customForm.name,
type: resolvedModelType,
url: customForm.url,
apiKey: customForm.apiKey.trim() === "" ? "sk-no-api-key" : customForm.apiKey,
frontend/app/[locale]/models/components/model/ModelAdvancedSettings.tsx:38
- This documentation says temperature, top_p, and extra_params are not included, but the same component classifies temperature/top_p as dedicated keys and
buildInferenceParamsPayloadsends them. The contradiction makes the storage contract misleading and can lead future changes to remove working fields; update the comment to describe dedicated columns plus extra_params JSONB accurately.
// - temperature / top_p / extra_params and other v2.6.0 additions are NOT
// included — the new dialog matches the original ModelAddDialog's parameter
// set without adding new inference parameters.
frontend/services/modelService.ts:387
- The method now advertises
typeas optional and says each model carries its own type, but/model/batch_createstill validatesBatchCreateModelsRequest.typeas required and the backend batch service overwrites every model'smodel_typewith the top-level value. Omittingtypetherefore yields a 422, while per-model types are not honored; update the backend contract/service or keep this field required.
frontend/services/modelService.ts:1301 - The manage batch endpoint has the same contract mismatch: this signature allows
typeto be omitted, butManageBatchCreateModelsRequest.typeis required andbatch_create_models_for_tenantindexes the key unconditionally. A caller following this type definition receives a validation error before the request reaches the service; either update the backend for per-row types or keeptyperequired.
frontend/services/modelService.ts:115 - This helper is used by the update paths as well as create. Clearing an existing field in
ModelEditDialogV2removes it frombuildInferenceParamsPayload, sotemperature,top_p, or an emptiedextra_paramsmap becomesundefinedhere and is omitted from the PATCH-like request; the backend then leaves the old database value intact. Update requests need an explicit clear representation (for examplenull) distinct from an omitted field.
frontend/services/modelService.ts:1406 - This manage-list method also advertises an optional
type, but/model/manage/provider/liststill usesManageProviderModelListRequest.model_typeas a required field. A caller following this signature withouttypereceives a 422; keep it required or update that request model and the service to support all-type discovery.
frontend/types/modelConfig.ts:240 - The backend catalog profile emits
chunk_batch(seeModelCatalogProfileandmodel_dump()), but this interface declareschunking_batch_sizeandgetFullCatalog()performs no mapping. The documented 1:1 API type is therefore wrong and typed consumers reading the declared field will getundefined.
- Files reviewed: 47/50 changed files
- Comments generated: 11
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Real bugs (blocked the unit-test CI job): - test_model_consts.py: corrupted multi-byte string literal (truncated mid-character) made the module unparseable; restore the intended default title assertion. - test_model_catalog_loader.py: read p.provider_key / get_model_profile(p.provider_key, ...) but the Pydantic ModelCatalogProviderInfo model exposes id; align to the actual field. Latent issues flagged by review: - model_managment_app.py: the catalog-import fallback handler logged via the module logger before it was initialized, raising NameError on the graceful-degradation path; log via logging.getLogger directly. - useModelCatalog.ts + types: provider summary declared provider_key/supported_model_types while /catalog/providers serializes id/supported_types (Pydantic dump). Align the frontend type and hook maps to the wire names. ModelCatalogModelEntry.provider_key stays as is - the /catalog/all model entries do carry provider_key.
The loader normalizes each catalog model entry into a ModelCatalogProfile instance (not a raw dict), so asserting model_type in model_cfg fails with TypeError on the Pydantic model. Accept both the raw dict form and the normalized instance via getattr. Verified in-container: 8 passed.
…3000 and project config persistence
…ked by model_management_db import chain
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
…re TLS verification
…ion, warnings, duplication)
…ged by SonarCloud
…, resolve remaining SonarCloud issues
… max-tokens logic
…arams_override with develop formatting
…topwords loading race-tolerant
…l priority UI in agent-prompt
模型配置修改