mirror of
https://github.com/langgenius/dify.git
synced 2026-07-21 02:28:30 +08:00
refactor(api): remove remaining legacy field remnants (#37967)
This commit is contained in:
parent
74f177efe6
commit
9465dc2477
@ -27,6 +27,7 @@ from controllers.console.wraps import (
|
||||
)
|
||||
from extensions.ext_database import db
|
||||
from fields.file_fields import FileResponse, UploadConfig
|
||||
from libs.helper import dump_response
|
||||
from libs.login import login_required
|
||||
from models import Account
|
||||
from services.file_service import FileService
|
||||
@ -117,8 +118,7 @@ class FileApi(Resource):
|
||||
except services.errors.file.BlockedFileExtensionError as blocked_extension_error:
|
||||
raise BlockedFileExtensionError(blocked_extension_error.description)
|
||||
|
||||
response = FileResponse.model_validate(upload_file, from_attributes=True)
|
||||
return response.model_dump(mode="json"), 201
|
||||
return dump_response(FileResponse, upload_file), 201
|
||||
|
||||
|
||||
@console_ns.route("/files/<uuid:file_id>/preview")
|
||||
@ -131,7 +131,7 @@ class FilePreviewApi(Resource):
|
||||
def get(self, current_tenant_id: str, file_id: UUID):
|
||||
file_id_str = str(file_id)
|
||||
text = FileService(db.engine).get_file_preview(file_id_str, current_tenant_id)
|
||||
return {"content": text}
|
||||
return TextContentResponse(content=text).model_dump(mode="json")
|
||||
|
||||
|
||||
@console_ns.route("/files/support-type")
|
||||
@ -141,4 +141,4 @@ class FileSupportTypeApi(Resource):
|
||||
@account_initialization_required
|
||||
@console_ns.response(200, "Success", console_ns.models[AllowedExtensionsResponse.__name__])
|
||||
def get(self):
|
||||
return {"allowed_extensions": list(DOCUMENT_EXTENSIONS)}
|
||||
return AllowedExtensionsResponse(allowed_extensions=list(DOCUMENT_EXTENSIONS)).model_dump(mode="json")
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import logging
|
||||
from collections.abc import Mapping
|
||||
from typing import Any
|
||||
|
||||
from flask_restx import Resource
|
||||
from pydantic import RootModel
|
||||
from pydantic import Field, RootModel
|
||||
|
||||
from controllers.common.schema import register_response_schema_models
|
||||
from controllers.console.wraps import (
|
||||
@ -10,6 +11,7 @@ from controllers.console.wraps import (
|
||||
setup_required,
|
||||
)
|
||||
from core.schemas.schema_manager import SchemaManager
|
||||
from fields.base import ResponseModel
|
||||
from libs.login import login_required
|
||||
|
||||
from . import console_ns
|
||||
@ -17,11 +19,17 @@ from . import console_ns
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SchemaDefinitionsResponse(RootModel[Any]):
|
||||
root: Any
|
||||
class SchemaDefinitionItemResponse(ResponseModel):
|
||||
name: str
|
||||
label: str
|
||||
schema_: Mapping[str, Any] = Field(alias="schema")
|
||||
|
||||
|
||||
register_response_schema_models(console_ns, SchemaDefinitionsResponse)
|
||||
class SchemaDefinitionsResponse(RootModel[list[SchemaDefinitionItemResponse]]):
|
||||
pass
|
||||
|
||||
|
||||
register_response_schema_models(console_ns, SchemaDefinitionItemResponse, SchemaDefinitionsResponse)
|
||||
|
||||
|
||||
@console_ns.route("/spec/schema-definitions")
|
||||
|
||||
@ -2,11 +2,11 @@ from typing import Any, Union
|
||||
|
||||
from flask import Response, request
|
||||
from flask_restx import Resource
|
||||
from pydantic import BaseModel, Field, ValidationError
|
||||
from pydantic import BaseModel, Field, RootModel, ValidationError
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session, sessionmaker
|
||||
|
||||
from controllers.common.schema import register_schema_model
|
||||
from controllers.common.schema import register_response_schema_models, register_schema_model
|
||||
from controllers.mcp import mcp_ns
|
||||
from core.mcp import types as mcp_types
|
||||
from core.mcp.server.streamable_http import handle_mcp_request, negotiate_protocol_version
|
||||
@ -33,7 +33,12 @@ class MCPRequestPayload(BaseModel):
|
||||
id: int | str | None = Field(default=None, description="Request ID for tracking responses")
|
||||
|
||||
|
||||
class MCPJSONRPCResponse(RootModel[mcp_types.JSONRPCResponse | mcp_types.JSONRPCError]):
|
||||
pass
|
||||
|
||||
|
||||
register_schema_model(mcp_ns, MCPRequestPayload)
|
||||
register_response_schema_models(mcp_ns, MCPJSONRPCResponse)
|
||||
|
||||
|
||||
@mcp_ns.route("/server/<string:server_code>/mcp")
|
||||
@ -42,13 +47,10 @@ class MCPAppApi(Resource):
|
||||
@mcp_ns.doc("handle_mcp_request")
|
||||
@mcp_ns.doc(description="Handle Model Context Protocol (MCP) requests for a specific server")
|
||||
@mcp_ns.doc(params={"server_code": "Unique identifier for the MCP server"})
|
||||
@mcp_ns.doc(
|
||||
responses={
|
||||
200: "MCP response successfully processed",
|
||||
400: "Invalid MCP request or parameters",
|
||||
404: "Server or app not found",
|
||||
}
|
||||
)
|
||||
@mcp_ns.response(200, "MCP JSON-RPC response", mcp_ns.models[MCPJSONRPCResponse.__name__])
|
||||
@mcp_ns.response(202, "MCP notification accepted")
|
||||
@mcp_ns.response(400, "Invalid MCP request or parameters")
|
||||
@mcp_ns.response(404, "Server or app not found")
|
||||
def post(self, server_code: str):
|
||||
"""Handle MCP requests for a specific server.
|
||||
|
||||
@ -64,6 +66,7 @@ class MCPAppApi(Resource):
|
||||
Raises:
|
||||
ValidationError: Invalid request format or parameters
|
||||
"""
|
||||
# response-contract:ignore MCP route returns Flask Response from JSON-RPC handler
|
||||
args = MCPRequestPayload.model_validate(mcp_ns.payload or {})
|
||||
request_id: Union[int, str] | None = args.id
|
||||
mcp_request = self._parse_mcp_request(args.model_dump(exclude_none=True))
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import re
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from typing import Union
|
||||
from typing import Literal, overload
|
||||
from urllib.parse import unquote
|
||||
|
||||
from configs import dify_config
|
||||
@ -40,10 +40,22 @@ USER_AGENT = (
|
||||
|
||||
|
||||
class ExtractProcessor:
|
||||
@overload
|
||||
@classmethod
|
||||
def load_from_upload_file(
|
||||
cls, upload_file: UploadFile, return_text: Literal[True], is_automatic: bool = False
|
||||
) -> str: ...
|
||||
|
||||
@overload
|
||||
@classmethod
|
||||
def load_from_upload_file(
|
||||
cls, upload_file: UploadFile, return_text: Literal[False] = False, is_automatic: bool = False
|
||||
) -> list[Document]: ...
|
||||
|
||||
@classmethod
|
||||
def load_from_upload_file(
|
||||
cls, upload_file: UploadFile, return_text: bool = False, is_automatic: bool = False
|
||||
) -> Union[list[Document], str]:
|
||||
) -> list[Document] | str:
|
||||
extract_setting = ExtractSetting(
|
||||
datasource_type=DatasourceType.FILE, upload_file=upload_file, document_model="text_model"
|
||||
)
|
||||
@ -53,8 +65,16 @@ class ExtractProcessor:
|
||||
else:
|
||||
return cls.extract(extract_setting, is_automatic)
|
||||
|
||||
@overload
|
||||
@classmethod
|
||||
def load_from_url(cls, url: str, return_text: bool = False) -> Union[list[Document], str]:
|
||||
def load_from_url(cls, url: str, return_text: Literal[True]) -> str: ...
|
||||
|
||||
@overload
|
||||
@classmethod
|
||||
def load_from_url(cls, url: str, return_text: Literal[False] = False) -> list[Document]: ...
|
||||
|
||||
@classmethod
|
||||
def load_from_url(cls, url: str, return_text: bool = False) -> list[Document] | str:
|
||||
response = remote_fetcher.make_request("GET", url, headers={"User-Agent": USER_AGENT})
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
from typing import override
|
||||
|
||||
from flask_restx import fields
|
||||
|
||||
from graphon.file import File
|
||||
|
||||
|
||||
class FilesContainedField(fields.Raw):
|
||||
@override
|
||||
def format(self, value):
|
||||
return self._format_file_object(value)
|
||||
|
||||
def _format_file_object(self, v):
|
||||
if isinstance(v, File):
|
||||
return v.model_dump()
|
||||
if isinstance(v, dict):
|
||||
return {k: self._format_file_object(vv) for k, vv in v.items()}
|
||||
if isinstance(v, list):
|
||||
return [self._format_file_object(vv) for vv in v]
|
||||
return v
|
||||
@ -20681,11 +20681,19 @@ Whitelist scopes accepted by RBAC app and dataset access config APIs.
|
||||
| last_id | string | | No |
|
||||
| limit | integer, <br>**Default:** 20 | | No |
|
||||
|
||||
#### SchemaDefinitionItemResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| label | string | | Yes |
|
||||
| name | string | | Yes |
|
||||
| schema | object | | Yes |
|
||||
|
||||
#### SchemaDefinitionsResponse
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
| ---- | ---- | ----------- | -------- |
|
||||
| SchemaDefinitionsResponse | | | |
|
||||
| SchemaDefinitionsResponse | array | | |
|
||||
|
||||
#### SegmentAttachmentResponse
|
||||
|
||||
|
||||
@ -1424,6 +1424,7 @@ class SummaryIndexService:
|
||||
- generating: Number of summaries being generated
|
||||
- error: Number of summaries with errors
|
||||
- not_started: Number of segments without summary records
|
||||
- timeout: Number of summaries that timed out
|
||||
- summaries: List of summary records with status and content preview
|
||||
"""
|
||||
from services.dataset_service import SegmentService
|
||||
|
||||
@ -11,7 +11,17 @@ class TestSpecSchemaDefinitionsApi:
|
||||
api = spec_module.SpecSchemaDefinitionsApi()
|
||||
method = unwrap(api.get)
|
||||
|
||||
schema_definitions = [{"type": "string"}]
|
||||
schema_definitions = [
|
||||
{
|
||||
"name": "conversation-variable",
|
||||
"label": "Conversation variable",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {"name": {"type": "string"}},
|
||||
"required": ["name"],
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
with patch.object(
|
||||
spec_module,
|
||||
@ -23,6 +33,12 @@ class TestSpecSchemaDefinitionsApi:
|
||||
|
||||
assert status == 200
|
||||
assert resp == schema_definitions
|
||||
assert spec_module.SchemaDefinitionsResponse.model_validate(resp).model_dump(mode="json") == schema_definitions
|
||||
|
||||
def test_get_documents_tight_response_model(self):
|
||||
response = spec_module.SpecSchemaDefinitionsApi.get.__apidoc__["responses"]["200"]
|
||||
|
||||
assert response[1].name == spec_module.SchemaDefinitionsResponse.__name__
|
||||
|
||||
def test_get_exception_returns_empty_list(self, caplog: pytest.LogCaptureFixture):
|
||||
api = spec_module.SpecSchemaDefinitionsApi()
|
||||
|
||||
@ -4,7 +4,15 @@ export type ClientOptions = {
|
||||
baseUrl: `${string}://${string}/console/api` | (string & {})
|
||||
}
|
||||
|
||||
export type SchemaDefinitionsResponse = unknown
|
||||
export type SchemaDefinitionsResponse = Array<SchemaDefinitionItemResponse>
|
||||
|
||||
export type SchemaDefinitionItemResponse = {
|
||||
label: string
|
||||
name: string
|
||||
schema: {
|
||||
[key: string]: unknown
|
||||
}
|
||||
}
|
||||
|
||||
export type GetSpecSchemaDefinitionsData = {
|
||||
body?: never
|
||||
|
||||
@ -2,10 +2,19 @@
|
||||
|
||||
import * as z from 'zod'
|
||||
|
||||
/**
|
||||
* SchemaDefinitionItemResponse
|
||||
*/
|
||||
export const zSchemaDefinitionItemResponse = z.object({
|
||||
label: z.string(),
|
||||
name: z.string(),
|
||||
schema: z.record(z.string(), z.unknown()),
|
||||
})
|
||||
|
||||
/**
|
||||
* SchemaDefinitionsResponse
|
||||
*/
|
||||
export const zSchemaDefinitionsResponse = z.unknown()
|
||||
export const zSchemaDefinitionsResponse = z.array(zSchemaDefinitionItemResponse)
|
||||
|
||||
/**
|
||||
* Success
|
||||
|
||||
Loading…
Reference in New Issue
Block a user