feat:接入mcp,为模型提供图表信息

This commit is contained in:
2026-10-01 06:33:44 -07:00
parent 618290d852
commit f1b2a70ad1
44 changed files with 6531 additions and 207 deletions
+12
View File
@@ -0,0 +1,12 @@
{
"mcpServers": {
"docs-langchain": {
"type": "http",
"url": "https://docs.langchain.com/mcp"
},
"reference-langchain": {
"type": "http",
"url": "https://reference.langchain.com/mcp"
}
}
}
+2 -2
View File
@@ -165,9 +165,9 @@ def plan_materials(state: QueryState) -> list[dict]:
if not types:
return []
# 按类型提取全部匹配物料,使用协议别名输出 schema 等字段
# 按类型提取全部匹配物料,使用协议别名输出 schema 等字段;字段定义由查询工具按需提供
return [
item.model_dump(mode="json", by_alias=True)
item.model_dump(mode="json", by_alias=True, exclude={"field_groups"})
for item in state["component_resources"].components
if item.type in types
]
+35 -15
View File
@@ -3,9 +3,37 @@
from copy import deepcopy
from app.actions.base import BaseActionHandler
from app.resources.material_fields import editable_fields, known_prop_keys
from app.schemas.actions import UpdateAction
from app.schemas.screen import Screen
from app.schemas.screen import Screen, ScreenNode
def fit_existing_path(node: ScreenNode, action: UpdateAction) -> UpdateAction:
"""字段定义允许的路径在组件中可能还没有中间对象(例如未开启过的数值标签)
前端只按已有路径赋值,这里把缺失的部分折叠进值里:改为更新最近的已有对象,
值为补齐的嵌套对象;缺失的若是数组元素则无法补齐,保持原样交给执行时报错
"""
if action.section == "name":
return action
current = getattr(node, action.section) or {}
for index, segment in enumerate(action.path[:-1]):
if isinstance(current, dict) and isinstance(segment, str) and segment not in current:
rest = action.path[index + 1 :]
if not all(isinstance(item, str) for item in rest):
return action
value = action.value
for item in reversed(rest):
value = {item: value}
return action.model_copy(
update={"path": action.path[: index + 1], "value": value}
)
if isinstance(current, dict) and isinstance(segment, str):
current = current[segment]
elif isinstance(current, list) and type(segment) is int and segment < len(current):
current = current[segment]
else:
return action
return action
class UpdateHandler(BaseActionHandler[UpdateAction]):
@@ -13,23 +41,15 @@ class UpdateHandler(BaseActionHandler[UpdateAction]):
# 确认目标存在,后续路径访问和赋值均在大屏副本中进行
result, target = self.copy_target(screen, action.target_id)
# 组件名称是节点顶层字段,没有后续路径
if action.section == "name":
target.name = action.value
return result
# 未设置的 style 视为空对象,只允许直接添加末级字段
if action.section == "style" and target.style is None:
target.style = {}
# props 第一层不能新造字段:渲染器不会读取约定外的字段,新造只会“成功”却没有效果
if action.section == "props":
known = known_prop_keys(target)
if known is not None and action.path[0] not in known:
labels = "、".join(
f"{field['label']}={field['section']}.{field['path']}"
for field in editable_fields(target.type)
)
raise ValueError(
f"组件 {target.type} 的 props 没有字段 {action.path[0]!r},"
f"不能新造组件级属性;可编辑字段:{labels or '无'}"
)
# 获取动作指定的属性区域,作为路径遍历的起点
current = getattr(target, action.section)
# 逐段访问路径,只有到达末级时才写入新值
+4 -4
View File
@@ -5,15 +5,15 @@ from fastapi import APIRouter
from app.common.response import ApiResponse, success_response
from app.core.exceptions import BizException
from app.resources.component import get_catalog_cache
from app.schemas.component_resource import ComponentCatalog
from app.resources.material_fields import frontend_catalog
router = APIRouter(prefix="/materials", tags=["Material"])
@router.get("")
async def list_materials() -> ApiResponse[ComponentCatalog]:
# 直接返回启动时载入的内存缓存,不查库
async def list_materials() -> ApiResponse[dict]:
# 直接使用启动时载入的内存缓存,不查库;属性面板 setters 由字段定义派生
catalog = get_catalog_cache()
if catalog is None:
raise BizException(503, "物料目录尚未加载")
return success_response(catalog)
return success_response(frontend_catalog(catalog))
+118
View File
@@ -0,0 +1,118 @@
"""物料字段 MCP 客户端:把字段查询工具交给模型使用
服务只监听本机,由应用启动时拉起;不可用时直接报错,不退回其他查询方式
"""
import asyncio
import os
import socket
import subprocess
import sys
from pathlib import Path
from urllib.parse import urlsplit
from langchain_core.tools import BaseTool
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_mcp_adapters.interceptors import MCPToolCallRequest
from loguru import logger
from mcp.types import CallToolResult
from app.config import settings
from app.resources.component import get_catalog_cache
SERVER_NAME = "materials"
PROJECT_ROOT = Path(__file__).resolve().parents[2]
class MaterialFieldServiceError(RuntimeError):
"""物料字段服务不可用,或与主进程的物料目录版本不一致"""
async def _check_version(request: MCPToolCallRequest, handler):
"""限制单次查询耗时;返回的目录版本须与主进程一致,否则模型查到的字段和校验依据不同"""
try:
result = await asyncio.wait_for(
handler(request), timeout=settings.mcp_tool_timeout
)
except TimeoutError as error:
raise MaterialFieldServiceError(f"物料字段查询超时: {request.name}") from error
if isinstance(result, CallToolResult) and result.structuredContent:
served = result.structuredContent.get("catalog_version")
catalog = get_catalog_cache()
expected = catalog.version if catalog is not None else None
if served != expected:
raise MaterialFieldServiceError(
f"物料字段服务的目录版本 {served} 与主服务 {expected} 不一致,请重启服务"
)
return result
_client = MultiServerMCPClient(
{
SERVER_NAME: {
"transport": "streamable_http",
"url": settings.mcp_material_url,
}
},
tool_interceptors=[_check_version],
# 未知组件类型、分组等查询错误返回给模型自行修正;连接失败等仍直接抛出
handle_tool_errors=True,
)
_tools: list[BaseTool] | None = None
async def get_material_tools() -> list[BaseTool]:
"""字段查询工具;首次使用或此前连接失败时向服务拉取工具清单"""
global _tools
if _tools is None:
try:
_tools = await asyncio.wait_for(
_client.get_tools(server_name=SERVER_NAME),
timeout=settings.mcp_tool_timeout,
)
except Exception as error:
raise MaterialFieldServiceError(
f"物料字段服务不可用: {settings.mcp_material_url}"
) from error
return _tools
def _port_open(port: int) -> bool:
with socket.socket() as probe:
probe.settimeout(0.2)
return probe.connect_ex(("127.0.0.1", port)) == 0
async def start_material_server() -> subprocess.Popen | None:
"""本机地址且尚未运行时拉起服务,返回进程供退出时关闭;已运行则直接复用"""
target = urlsplit(settings.mcp_material_url)
if target.hostname not in {"localhost", "127.0.0.1"}:
raise MaterialFieldServiceError("物料字段服务只能部署在本机,不对外暴露")
port = target.port or 80
if _port_open(port):
return None
process = subprocess.Popen(
[sys.executable, "-X", "utf8", "-m", "mcp_servers.material_server"],
cwd=PROJECT_ROOT,
env={**os.environ, "PORT": str(port)},
)
for _ in range(100):
if process.poll() is not None:
raise MaterialFieldServiceError("物料字段服务启动失败,请查看终端日志")
if _port_open(port):
logger.info("物料字段服务已启动: {}", settings.mcp_material_url)
return process
await asyncio.sleep(0.1)
await stop_material_server(process)
raise MaterialFieldServiceError("物料字段服务启动超时")
async def stop_material_server(process: subprocess.Popen | None) -> None:
if process is None or process.poll() is not None:
return
process.terminate()
try:
await asyncio.to_thread(process.wait, timeout=5)
except subprocess.TimeoutExpired:
process.kill()
await asyncio.to_thread(process.wait)
+19
View File
@@ -58,6 +58,21 @@ class Settings:
default_factory=lambda: int(os.getenv("MAX_ACTION_RETRIES", "2"))
)
# 物料字段 MCP 服务:只给模型查询字段定义,只监听本机,随应用启动
mcp_material_url: str = field(
default_factory=lambda: (
os.getenv("MCP_MATERIAL_URL") or "http://127.0.0.1:8201/mcp"
)
)
# 单次字段查询的超时(秒)
mcp_tool_timeout: float = field(
default_factory=lambda: float(os.getenv("MCP_TOOL_TIMEOUT", "5"))
)
# 生成动作或修正建议时,最多几轮查询字段,之后必须给出结果
max_field_tool_rounds: int = field(
default_factory=lambda: int(os.getenv("MAX_FIELD_TOOL_ROUNDS", "4"))
)
# 没有任何对话的空会话保留时长(秒),超时后视为不存在
conversation_empty_ttl: int = field(
default_factory=lambda: int(os.getenv("CONVERSATION_EMPTY_TTL", "7200"))
@@ -113,6 +128,10 @@ class Settings:
raise ValueError("CONVERSATION_HISTORY_TURNS 必须是非负整数")
if type(self.max_action_retries) is not int or self.max_action_retries < 0:
raise ValueError("MAX_ACTION_RETRIES 必须是非负整数")
if self.mcp_tool_timeout <= 0:
raise ValueError("MCP_TOOL_TIMEOUT 必须是正数")
if self.max_field_tool_rounds < 0:
raise ValueError("MAX_FIELD_TOOL_ROUNDS 必须是非负整数")
if not 0 <= self.intent_min_probability <= 1:
raise ValueError("INTENT_MIN_PROBABILITY 必须在 0 到 1 之间")
if not 0 <= self.intent_qa_min_probability <= 1:
+19 -6
View File
@@ -18,6 +18,11 @@ from app.core.logging import setup_logging
async def lifespan(application: FastAPI):
setup_logging()
from app.clients.decision import close_decision_engine
from app.clients.material_mcp import (
MaterialFieldServiceError,
start_material_server,
stop_material_server,
)
from app.clients.user_center import close_user_center
from app.observability.tracing import flush_tracing
from app.processor.graph import build_graph
@@ -28,9 +33,15 @@ async def lifespan(application: FastAPI):
# 按配置选择 MongoDB 或进程内存,会话记录与图检查点使用同一种存储
persistence = await open_persistence()
application.state.conversation_store = persistence.store
material_server = None
try:
# 物料目录启动时载入内存,agent 规划与前端接口共用,避免重复查询数据库
await warm_material_cache(persistence.db)
# 物料字段服务给模型查询字段;启动失败只记录,需要查字段的修改执行时会报错
try:
material_server = await start_material_server()
except MaterialFieldServiceError as error:
logger.error("{},修改组件属性和新增组件将无法执行", error)
application.state.agent_service = AgentService(
build_graph(persistence.checkpointer), persistence.store
)
@@ -38,19 +49,21 @@ async def lifespan(application: FastAPI):
finally:
# 共享客户端只在应用退出时关闭,追踪刷新放到线程避免阻塞事件循环
try:
await persistence.close()
await stop_material_server(material_server)
finally:
try:
await close_decision_engine()
await persistence.close()
finally:
try:
await close_user_center()
await close_decision_engine()
finally:
try:
await asyncio.to_thread(flush_tracing)
await close_user_center()
finally:
await logger.complete()
try:
await asyncio.to_thread(flush_tracing)
finally:
await logger.complete()
def create_app() -> FastAPI:
application = FastAPI(title="Screen Agent API", lifespan=lifespan)
+91
View File
@@ -0,0 +1,91 @@
"""需要物料字段的模型调用:模型按需调用字段查询工具,最后按指定结构输出
动作生成和修正建议共用;字段查询走物料字段 MCP 服务,服务不可用时直接报错
"""
from typing import TypeVar
from langchain.agents import create_agent
from langchain.agents.middleware import wrap_model_call
from langchain.agents.structured_output import ToolStrategy
from langchain_core.messages import AIMessage, HumanMessage
from pydantic import BaseModel
from app.clients.llm import get_llm
from app.clients.material_mcp import get_material_tools
from app.config import settings
from app.processor.state import QueryState
from app.resources.component import get_catalog_cache
from app.resources.material_fields import list_field_groups
OutputModel = TypeVar("OutputModel", bound=BaseModel)
@wrap_model_call
async def _limit_field_queries(request, handler):
"""查询轮数用尽后只保留输出工具,模型必须给出结果"""
rounds = sum(isinstance(message, AIMessage) for message in request.messages)
if rounds >= settings.max_field_tool_rounds:
request = request.override(tools=[])
return await handler(request)
async def invoke_with_field_tools(
schema: type[OutputModel], system_prompt: str, content: str
) -> OutputModel | None:
"""返回结构化结果;模型未给出结果时返回 None
结果不符合 schema 时抛出 StructuredOutputValidationError,由调用方转换为校验错误
"""
agent = create_agent(
get_llm(),
await get_material_tools(),
system_prompt=system_prompt,
# 不在调用内自动重试,结构错误交给后续校验和修正流程,与不查字段时一致
response_format=ToolStrategy(schema, handle_errors=False),
middleware=[_limit_field_queries],
name=f"{schema.__name__}_with_fields",
)
result = await agent.ainvoke(
{"messages": [HumanMessage(content=content)]},
# 每轮模型调用和工具执行各占一步,轮数由中间件限制,这里只防止异常循环
config={"recursion_limit": 2 * settings.max_field_tool_rounds + 6},
)
return result.get("structured_response")
def field_group_directory(state: QueryState) -> dict[str, dict]:
"""本次相关组件的字段分组目录,按物料类型列出,省去模型先查目录的一轮调用"""
nodes = {node.id: node.type for node in state["input_context"].screen.nodes}
plan = state.get("screen_plan")
if plan is None:
types = [
nodes[target]
for change in state["requirement"].changes
if change.target_scope == "nodes"
for target in change.target_ids
if target in nodes
]
else:
types = [
item.component_type
if item.disposition in ("add", "replace")
else nodes.get(item.target_id)
for item in plan.components
if item.disposition in ("add", "replace", "modify")
]
catalog = get_catalog_cache()
directory = {}
for component_type in dict.fromkeys(item for item in types if item):
try:
if catalog is None:
raise ValueError("物料目录未载入")
directory[component_type] = {
key: value
for key, value in list_field_groups(catalog, component_type).items()
if key not in ("component_type", "catalog_version")
}
except ValueError as error:
# 没有物料定义的组件不能修改属性,告诉模型原因
directory[component_type] = {"error": str(error)}
return directory
+39 -49
View File
@@ -2,6 +2,10 @@
import json
from langchain.agents.structured_output import (
StructuredOutputError,
StructuredOutputValidationError,
)
from langchain_core.exceptions import OutputParserException
from langchain_core.messages import HumanMessage, SystemMessage
from pydantic import ValidationError
@@ -9,13 +13,18 @@ from pydantic import ValidationError
from app.actions.plan import assemble_plan_actions, plan_block_reason, plan_materials
from app.clients.llm import get_llm
from app.processor.base import BaseNode
from app.processor.material_agent import field_group_directory, invoke_with_field_tools
from app.processor.state import QueryState
from app.prompt.action import build_action_prompt
from app.resources.material_fields import editable_fields
from app.prompt.action import build_action_prompt, uses_material_fields
from app.schemas.action_validation import ActionValidationError, schema_errors
from app.schemas.actions import ActionBatch, AddAction
from app.schemas.operations import is_direct_operation
GENERATION_ERROR = ActionValidationError(
code="generation_error",
message="模型输出无法解析为 ActionBatch,请按协议重新生成",
)
def can_generate_actions(state: QueryState) -> bool:
# 没有阻塞原因时才允许生成动作,路由和节点入口复用同一套能力检查
@@ -59,28 +68,6 @@ def action_block_reason(state: QueryState) -> str | None:
return None
def update_fields(state: QueryState) -> dict[str, list[dict]]:
"""按组件 ID 汇总可能被属性更新的已有组件的可编辑字段,没有字段的组件省略"""
plan = state.get("screen_plan")
if plan is None:
target_ids = [
target
for change in state["requirement"].changes
if change.target_scope == "nodes"
for target in change.target_ids
]
else:
target_ids = [
item.target_id for item in plan.components if item.disposition == "modify"
]
nodes = {node.id: node for node in state["input_context"].screen.nodes}
result = {}
for target in dict.fromkeys(target_ids):
if target in nodes and (fields := editable_fields(nodes[target].type)):
result[target] = fields
return result
class ActionNode(BaseNode):
name = "action_node"
@@ -115,10 +102,6 @@ class ActionNode(BaseNode):
payload["screen_plan"] = state["screen_plan"].model_dump(mode="json")
payload["component_materials"] = plan_materials(state)
# 提供待修改组件的属性面板字段,让模型把用户说的“标题”“颜色”等对应到真实路径
if fields := update_fields(state):
payload["editable_fields"] = fields
# 如果本轮是动作修正,提供修正建议、上一轮动作和校验错误供模型参考
if state.get("repair_plan") is not None:
payload.update(
@@ -133,7 +116,17 @@ class ActionNode(BaseNode):
],
)
llm_client = get_llm(ActionBatch)
system_prompt = build_action_prompt(
state["requirement"],
state.get("screen_plan"),
repairing=state.get("repair_plan") is not None,
)
# 需要写组件属性时提供相关组件的字段分组,模型再按需查询字段定义
with_fields = uses_material_fields(state["requirement"], state.get("screen_plan"))
if with_fields:
payload["field_groups"] = field_group_directory(state)
# 将本次具体资料放入用户消息,保留中文以便阅读和排查
content = json.dumps(payload, ensure_ascii=False)
# 旧动作及错误已放入模型输入,现在清空输出状态,避免本轮失败后残留旧结果
updated_state["actions"] = None
@@ -146,19 +139,12 @@ class ActionNode(BaseNode):
# 捕获输出结构和解析错误,转换成统一校验结果供后续修正流程使用
try:
# 异步调用模型,根据需求、规划及是否正在修正选择对应的系统规则
result = await llm_client.ainvoke(
[
SystemMessage(
content=build_action_prompt(
state["requirement"],
state.get("screen_plan"),
repairing=state.get("repair_plan") is not None,
)
),
# 将本次具体资料放入用户消息,保留中文以便阅读和排查
HumanMessage(content=json.dumps(payload, ensure_ascii=False)),
]
)
if with_fields:
result = await invoke_with_field_tools(ActionBatch, system_prompt, content)
else:
result = await get_llm(ActionBatch).ainvoke(
[SystemMessage(content=system_prompt), HumanMessage(content=content)]
)
# 响应中没有工具调用时解析器会返回 None
if result is None:
@@ -184,15 +170,19 @@ class ActionNode(BaseNode):
updated_state["validation_passed"] = False
updated_state["validation_errors"] = schema_errors(error)
return updated_state
except OutputParserException:
except StructuredOutputValidationError as error:
# 查询字段后的输出不符合结构,与直接输出时的处理一致
updated_state["validation_passed"] = False
updated_state["validation_errors"] = (
schema_errors(error.source)
if isinstance(error.source, ValidationError)
else [GENERATION_ERROR]
)
return updated_state
except (OutputParserException, StructuredOutputError):
# 如果模型输出无法解析,记录生成错误,提示后续按 ActionBatch 协议重新生成
updated_state["validation_passed"] = False
updated_state["validation_errors"] = [
ActionValidationError(
code="generation_error",
message="模型输出无法解析为 ActionBatch,请按协议重新生成",
)
]
updated_state["validation_errors"] = [GENERATION_ERROR]
return updated_state
# 保存生成的动作,实际目标、规划一致性及模拟执行结果仍由后续校验节点检查
+21 -10
View File
@@ -2,6 +2,7 @@
import json
from langchain.agents.structured_output import StructuredOutputError
from langchain_core.exceptions import OutputParserException
from langchain_core.messages import HumanMessage, SystemMessage
from pydantic import ValidationError
@@ -9,7 +10,9 @@ from pydantic import ValidationError
from app.actions.plan import plan_materials
from app.clients.llm import get_llm
from app.processor.base import BaseNode
from app.processor.material_agent import field_group_directory, invoke_with_field_tools
from app.processor.state import QueryState
from app.prompt.action import FIELD_QUERY_PROMPT, uses_material_fields
from app.schemas.capabilities import USER_FAILURE_MESSAGE
from app.prompt.reflection import REFLECTION_SYSTEM_PROMPT
from app.schemas.action_repair import ActionRepairPlan
@@ -22,7 +25,6 @@ class ReflectionNode(BaseNode):
# 浅拷贝状态
updated_state: QueryState = state.copy()
llm_client = get_llm(ActionRepairPlan)
# 汇总原始上下文、需求和失败信息,让模型基于实际错误提出修正建议
payload = {
"input_context": state["input_context"].model_dump(mode="json"),
@@ -46,21 +48,30 @@ class ReflectionNode(BaseNode):
payload["screen_plan"] = state["screen_plan"].model_dump(mode="json")
payload["component_materials"] = plan_materials(state)
# 涉及组件属性时,修正建议须基于查询到的字段定义,与动作生成使用同一组工具
with_fields = uses_material_fields(state["requirement"], state.get("screen_plan"))
if with_fields:
payload["field_groups"] = field_group_directory(state)
# 用户消息承载本轮待分析的具体资料,序列化时保留中文
content = json.dumps(payload, ensure_ascii=False)
# 只捕获输出结构和解析错误,配置、网络等异常继续向上抛出
try:
# 异步生成修正建议,本节点不直接修改动作
plan = await llm_client.ainvoke(
[
# 系统消息提供稳定的失败分析和修正规则
SystemMessage(content=REFLECTION_SYSTEM_PROMPT),
# 用户消息承载本轮待分析的具体资料,序列化时保留中文
HumanMessage(content=json.dumps(payload, ensure_ascii=False)),
]
)
if with_fields:
plan = await invoke_with_field_tools(
ActionRepairPlan,
REFLECTION_SYSTEM_PROMPT + "\n" + FIELD_QUERY_PROMPT,
content,
)
else:
plan = await get_llm(ActionRepairPlan).ainvoke(
[SystemMessage(content=REFLECTION_SYSTEM_PROMPT), HumanMessage(content=content)]
)
# 响应中没有工具调用时解析器会返回 None
if plan is None:
raise OutputParserException("模型未返回结构化结果")
except (ValidationError, OutputParserException):
except (ValidationError, OutputParserException, StructuredOutputError):
# 如果未能得到合法建议,清空旧修正计划,避免再次使用已失效的建议
updated_state["repair_plan"] = None
# 记录无法继续修正的原因,交给后续路由和失败处理流程
+36 -4
View File
@@ -4,10 +4,18 @@ from pydantic import ValidationError
from app.actions.plan import validate_plan_actions, validate_plan_result
from app.actions.registry import apply_action
from app.actions.update import fit_existing_path
from app.processor.base import BaseNode
from app.processor.state import QueryState
from app.resources.component import get_catalog_cache
from app.resources.material_fields import check_update_field
from app.schemas.action_validation import ActionValidationError, schema_errors
from app.schemas.actions import ActionBatch, AddAction, UpdateCanvasAction
from app.schemas.actions import (
ActionBatch,
AddAction,
UpdateAction,
UpdateCanvasAction,
)
from app.schemas.operations import is_canvas_operation
@@ -121,8 +129,32 @@ class ValidatorNode(BaseNode):
# 在副本上试执行,不改变用户传入的大屏,也不操作前端画布
screen = state["input_context"].screen.model_copy(deep=True)
catalog = get_catalog_cache()
# 保存补齐路径后的动作,前端按它执行
actions = list(batch.actions)
# 按动作顺序逐项执行,每一步都使用上一步更新后的大屏副本
for index, action in enumerate(batch.actions):
for index, action in enumerate(actions):
# 属性更新先按物料字段定义检查,目标不存在时交给处理器报错
node = (
next((item for item in screen.nodes if item.id == action.target_id), None)
if isinstance(action, UpdateAction)
else None
)
if node is not None:
try:
check_update_field(catalog, node.type, action)
except ValueError as error:
updated_state["validation_passed"] = False
updated_state["validation_errors"] = [
ActionValidationError(
action_index=index, code="field_error", message=str(error)
)
]
updated_state["simulated_screen"] = None
return updated_state
# 字段定义允许、但组件中还没有的中间对象,折叠进值里再执行
action = actions[index] = fit_existing_path(node, action)
try:
# 根据动作类型调用对应处理逻辑,检查目标和参数并返回更新后的大屏
screen = apply_action(screen, action)
@@ -147,8 +179,8 @@ class ValidatorNode(BaseNode):
updated_state["simulated_screen"] = None
return updated_state
# 全部检查通过后,保存重新校验过的动作对象,统一后续读取的数据结构
updated_state["actions"] = batch.actions
# 全部检查通过后,保存重新校验并补齐路径的动作对象,统一后续读取的数据结构
updated_state["actions"] = actions
# 标记整批动作校验通过,并清空之前可能残留的错误
updated_state["validation_passed"] = True
updated_state["validation_errors"] = []
+48 -21
View File
@@ -53,6 +53,46 @@ REPAIR_TASK_PROMPT = """# 当前阶段:修正重试
"""
FIELD_QUERY_PROMPT = """# 查询组件字段
field_groups 按物料类型列出本次相关组件的字段分组,节点的 type 就是物料类型
确定要改的字段:用用户原话中对属性的说法(input_context 中的提问,不用改写后的需求)调用 search_fields;
返回多个字段时,依据字段说明判断用户指的是哪一个;用户看的是大屏画面,没有提到图层列表、属性面板等编辑器位置时,按画面上看得到的内容理解
需要了解一组相关字段时用 get_fields,一次可查多个分组
只能使用查询到的字段路径,值须符合字段的类型、可选值和取值范围;查不到对应字段说明不支持,不自行编造路径
"""
# 这些动作会写组件属性,需要先查物料字段
FIELD_ACTIONS = frozenset({"update", "add"})
def action_types(requirement: Requirement, screen_plan: ScreenPlan | None) -> set[str]:
"""本次需要的动作类型:简单修改按需求变更,规划按各组件的处理方式"""
if screen_plan is None:
return {
"update_canvas" if change.target_scope == "canvas" else change.operation
for change in requirement.changes
}
types = set()
if screen_plan.canvas_changes:
types.add("update_canvas")
for item in screen_plan.components:
if item.disposition in ("add", "replace"):
types.add("add")
elif item.disposition == "remove":
types.add("delete")
elif item.disposition == "modify":
types.update(("move", "resize"))
if item.design:
types.add("update")
return types
def uses_material_fields(
requirement: Requirement, screen_plan: ScreenPlan | None
) -> bool:
return bool(action_types(requirement, screen_plan) & FIELD_ACTIONS)
def build_action_prompt(
requirement: Requirement,
screen_plan: ScreenPlan | None = None,
@@ -60,40 +100,27 @@ def build_action_prompt(
repairing: bool = False,
) -> str:
"""只组合本次任务需要的规则,不改变 Schema 或后端校验范围"""
types = action_types(requirement, screen_plan)
sections = [ACTION_SYSTEM_PROMPT]
if screen_plan is None:
sections.append(SIMPLE_TASK_PROMPT)
action_types = {
"update_canvas" if change.target_scope == "canvas" else change.operation
for change in requirement.changes
}
else:
sections.append(PLAN_TASK_PROMPT)
action_types = set()
dispositions = {item.disposition for item in screen_plan.components}
if screen_plan.canvas_changes:
sections.append(
"## 调整画布\n将 canvas_changes 中的最终属性值转换为 update_canvas,完整覆盖且不添加其他画布字段"
)
action_types.add("update_canvas")
if any(item.disposition == "add" for item in screen_plan.components):
action_types.add("add")
if any(item.disposition == "replace" for item in screen_plan.components):
if "replace" in dispositions:
sections.append(REPLACE_PLAN_PROMPT)
action_types.add("add")
if any(item.disposition == "remove" for item in screen_plan.components):
action_types.add("delete")
modifications = [
item for item in screen_plan.components if item.disposition == "modify"
]
if modifications:
if "modify" in dispositions:
sections.append(MODIFY_PLAN_PROMPT)
action_types.update(("move", "resize"))
if any(item.design for item in modifications):
action_types.add("update")
if types & FIELD_ACTIONS:
sections.append(FIELD_QUERY_PROMPT)
# 排序保证提示词顺序稳定,缺少动作说明时直接报错,避免静默遗漏
sections.append("# 本次可用动作\n" + "、".join(sorted(action_types)))
sections.extend(ACTION_RULES[name] for name in sorted(action_types))
sections.append("# 本次可用动作\n" + "、".join(sorted(types)))
sections.extend(ACTION_RULES[name] for name in sorted(types))
if repairing:
sections.append(REPAIR_TASK_PROMPT)
return "\n\n".join(section.strip() for section in sections)
+4 -6
View File
@@ -21,13 +21,11 @@ properties 只包含需要修改的画布顶层字段,宽高为最终绝对值
不需要改变的尺寸省略,不改变组件位置
""",
"update": """## update:更新已有组件属性
依据快照选择 props / style 下的字段路径,不猜测不存在的嵌套结构
path 中字符串是对象键,整数是数组下标;中间路径必须存在,数组下标不能越界
只替换指定字段,不做深度合并;null 表示赋值,不表示删除
提供 editable_fields 时,它是该组件属性面板的字段:用户说的名称(如名字、标题、颜色)优先对应 label 相符的字段,使用其 section 和 path
props 第一层只能使用组件已有或物料默认配置中的字段,不新造组件级属性;更深层的配置可以按需新增
字段路径的第一段写入 section(name / props / style),其余各段写入 path,数字段写成整数下标;name 的 path 为空列表
例如字段 props.option.title.text 写成 section=props、path=["option", "title", "text"]
layout 字段不用 update:位置用 move,尺寸用 resize
只替换指定字段,不做深度合并;组件中还没有的中间对象由后端补齐
不通过修改属性改变组件类型(如把 series 的 type 改成其他图表类型),组件类型由节点 type 和物料决定
style 为 null 时可用单层路径添加属性,保留未要求修改的配置
""",
"add": """## add:按物料新增组件
每个 add 规划对应一个新增动作,用 plan_id 关联规划,component_key 选择 component_materials 中的物料
+1
View File
@@ -15,6 +15,7 @@ REFLECTION_SYSTEM_PROMPT = f"""## 任务
需要用户补充、外部资源或尚未支持的能力时,retryable=false,说明缺少什么
{DATA_BINDING_GUIDE}
因数据绑定校验失败时,去掉 dataId 并保留物料示例数据即可修正,retryable=true
字段错误(field_error)时,查询字段定义,建议改用查到的字段路径或合法取值;物料没有能满足需求的字段时,retryable=false
## 边界
保持需求和规划范围,不通过遗漏需求、猜测字段或替换用户意图来绕过错误
+38 -6
View File
@@ -1,5 +1,7 @@
"""查询前端组件目录:目录只来自启动时从 MongoDB 载入的进程内存缓存,没有文件回退"""
import hashlib
import json
from pathlib import Path
from app.schemas.component_resource import (
@@ -21,14 +23,44 @@ def get_catalog_cache() -> ComponentCatalog | None:
return _cached_catalog
def load_catalog_file(path: str | Path) -> ComponentCatalog:
"""读取导出的目录文件,只供导入脚本使用,运行时不读取文件"""
# 如果传入的是相对路径,以项目根目录为基准,避免受启动时工作目录影响
target = Path(path)
# 手写的物料定义目录,只供导入脚本读取后写入 MongoDB,运行时不读取
MATERIALS_DIR = Path(__file__).resolve().parent / "materials"
FIELD_GROUPS_FILE = "field_groups.json"
def load_material_definitions(directory: str | Path = MATERIALS_DIR) -> ComponentCatalog:
"""读取物料定义目录:共享字段分组一个文件,每个物料一个文件;不合法直接报错
version 为全部文件内容的摘要,定义有任何改动都会得到新版本
"""
target = Path(directory)
if not target.is_absolute():
target = Path(__file__).resolve().parents[2] / target
# utf-8-sig 同时兼容带 BOM 和不带 BOM 的 UTF-8 文件;损坏或不合法直接报错
return ComponentCatalog.model_validate_json(target.read_text(encoding="utf-8-sig"))
files = sorted(target.glob("*.json"))
if not any(path.name == FIELD_GROUPS_FILE for path in files):
raise FileNotFoundError(f"物料定义目录缺少 {FIELD_GROUPS_FILE}: {target}")
digest = hashlib.sha256()
components, field_groups = [], []
for path in files:
# utf-8-sig 同时兼容带 BOM 和不带 BOM 的 UTF-8 文件
text = path.read_text(encoding="utf-8-sig")
digest.update(path.name.encode() + b"\0" + text.encode())
if path.name == FIELD_GROUPS_FILE:
field_groups = json.loads(text)
else:
components.append(json.loads(text))
# 文件名排序与展示顺序无关,物料按定义中的 order 排列,未写时排在最后
components.sort(key=lambda item: item.get("order", len(files)))
return ComponentCatalog.model_validate(
{
"version": f"sha256:{digest.hexdigest()}",
"components": [
{key: value for key, value in item.items() if key != "order"}
for item in components
],
"field_groups": field_groups,
}
)
class ComponentProvider:
+195 -39
View File
@@ -1,50 +1,206 @@
"""从物料目录读取组件的可编辑字段,供动作生成理解字段含义,并供校验约束属性结构"""
"""按物料展开字段定义:解析共享分组引用、取字段默认值、派生前端属性面板 setters"""
from app.resources.component import ComponentProvider
from app.schemas.component_resource import ComponentDefinition, ComponentQuery
from app.schemas.screen import ScreenNode
from pydantic import JsonValue
# 属性面板的 key 以区域开头,只有这两个区域可以被 update 修改
EDITABLE_SECTIONS = ("props", "style")
from app.schemas.component_resource import ComponentCatalog, ComponentDefinition
from app.schemas.material_field import (
MISSING,
PANEL_CONTROLS,
FieldGroup,
MaterialField,
lookup,
)
def _materials(node_type: str) -> list[ComponentDefinition]:
# 多个物料预设可以共用同一前端 type,按 type 取全部预设
result = ComponentProvider().query(ComponentQuery(type=node_type))
return result.components if result.status == "resolved" else []
def resolve_field_groups(
catalog: ComponentCatalog, component: ComponentDefinition
) -> list[FieldGroup]:
"""按物料声明的顺序展开字段分组,共享分组取目录中的定义;返回副本,不污染目录"""
shared = {group.key: group for group in catalog.field_groups}
return [
(shared[group] if isinstance(group, str) else group).model_copy(deep=True)
for group in component.field_groups
]
def _split_key(key: str) -> tuple[str, list[str | int]] | None:
# 前端点路径中的纯数字段是数组下标,转换为与 UpdateAction.path 一致的整数
section, *path = key.split(".")
if section not in EDITABLE_SECTIONS or not path:
return None
return section, [int(part) if part.isdigit() else part for part in path]
def field_default(component: ComponentDefinition, field: MaterialField) -> JsonValue:
"""物料默认配置中有该路径时取默认配置的值,否则取字段声明的 default"""
value = lookup(component.template(), field.segments)
return field.default if value is MISSING else value
def editable_fields(node_type: str) -> list[dict]:
"""返回组件属性面板中的字段:用户可见名称、区域、路径及可选值,按出现顺序去重"""
fields: dict[str, dict] = {}
for material in _materials(node_type):
for setter in material.setters:
parsed = _split_key(setter.key)
if parsed is None or setter.key in fields:
def panel_setters(groups: list[FieldGroup]) -> list[dict]:
"""派生前端属性面板的 setters,格式与前端 SetterSchema 一致"""
setters = []
for group in groups:
for field in group.fields:
if not field.panel:
continue
section, path = parsed
field = {"label": setter.label, "section": section, "path": path}
if options := setter.props.get("options"):
field["options"] = options
fields[setter.key] = field
return list(fields.values())
props = {}
if field.options:
props["options"] = [
{"label": option.label, "value": option.value} for option in field.options
]
setter = {
"type": PANEL_CONTROLS[field.value_type],
"label": field.label,
"key": field.path,
"props": props,
}
if field.span is not None:
setter["span"] = field.span
setters.append(setter)
return setters
def known_prop_keys(node: ScreenNode) -> set[str] | None:
"""props 第一层是组件与渲染器的约定,只能是节点已有或物料默认配置中的字段;
目录中找不到该类型时约定未知,返回 None 表示不做限制"""
materials = _materials(node.type)
if not materials:
return None
keys = set(node.props)
for material in materials:
keys.update(material.schema_.props)
return keys
def frontend_catalog(catalog: ComponentCatalog) -> dict:
"""前端物料接口的数据:在保存的定义上附加由字段定义派生的 setters"""
data = catalog.model_dump(mode="json", by_alias=True)
for item, component in zip(data["components"], catalog.components, strict=True):
item["setters"] = panel_setters(resolve_field_groups(catalog, component))
return data
# ---- 字段查询:MCP 工具、动作提示词和动作校验共用同一套逻辑 ----
def find_material(catalog: ComponentCatalog, component_type: str) -> ComponentDefinition:
"""按节点 type 找物料;找不到或对应多个物料时报错,不猜测"""
matches = [item for item in catalog.components if item.type == component_type]
if len(matches) != 1:
known = "、".join(item.type for item in catalog.components)
reason = "没有对应的物料" if not matches else "对应多个物料"
raise ValueError(f"组件类型 {component_type} {reason},可用类型:{known}")
return matches[0]
def describe_field(
component: ComponentDefinition, group: FieldGroup, field: MaterialField
) -> dict:
"""给模型看的字段说明:省略空值和属性面板专用的信息"""
data = field.model_dump(
mode="json",
exclude={"default", "panel", "span", "editable"},
exclude_none=True,
exclude_defaults=True,
)
data["default"] = field_default(component, field)
if not field.editable:
data["editable"] = False
data["group"] = group.key
return data
def list_field_groups(catalog: ComponentCatalog, component_type: str) -> dict:
"""物料的字段分组目录:每组的说明和字段名称,模型据此决定查看哪几组"""
component = find_material(catalog, component_type)
return {
"component_type": component.type,
"name": component.name,
"catalog_version": catalog.version,
"groups": [
{
"key": group.key,
"label": group.label,
"description": group.description,
"fields": [field.label for field in group.fields],
}
for group in resolve_field_groups(catalog, component)
],
}
def get_fields(
catalog: ComponentCatalog, component_type: str, group_keys: list[str]
) -> dict:
"""指定分组的完整字段定义"""
component = find_material(catalog, component_type)
groups = {group.key: group for group in resolve_field_groups(catalog, component)}
if unknown := [key for key in group_keys if key not in groups]:
raise ValueError(
f"{component.type} 没有字段分组 {unknown},可用分组:{list(groups)}"
)
return {
"component_type": component.type,
"catalog_version": catalog.version,
"fields": [
describe_field(component, groups[key], field)
for key in dict.fromkeys(group_keys)
for field in groups[key].fields
],
}
def _match_score(field: MaterialField, query: str) -> int:
"""名称、别名完全一致最相关,其次是互相包含、路径末段一致,最后是说明中出现"""
names = [name.casefold() for name in (field.label, *field.aliases)]
if query in names:
return 4
if any(query in name or name in query for name in names):
return 3
if query == field.path.rsplit(".", 1)[-1].casefold():
return 2
if query in field.description.casefold() or query in field.path.casefold():
return 1
return 0
def search_fields(
catalog: ComponentCatalog, component_type: str, query: str, limit: int = 8
) -> dict:
"""按名称、别名、路径和说明搜索字段,按相关度返回"""
component = find_material(catalog, component_type)
keyword = query.strip().casefold()
if not keyword:
raise ValueError("搜索内容不能为空")
scored = [
(score, index, group, field)
for index, (group, field) in enumerate(
(group, field)
for group in resolve_field_groups(catalog, component)
for field in group.fields
)
if (score := _match_score(field, keyword))
]
scored.sort(key=lambda item: (-item[0], item[1]))
return {
"component_type": component.type,
"catalog_version": catalog.version,
"query": query,
"fields": [
describe_field(component, group, field)
for _, _, group, field in scored[:limit]
],
}
def check_update_field(catalog: ComponentCatalog | None, node_type: str, action) -> None:
"""update 只能修改字段定义中的可编辑字段,值须符合字段类型和取值约束
不合法时抛 ValueError,路径不存在时附上可能对应的字段供修正参考
"""
if catalog is None:
raise ValueError("物料目录未载入,无法确认组件有哪些字段")
component = find_material(catalog, node_type)
fields = {
field.path: field
for group in resolve_field_groups(catalog, component)
for field in group.fields
}
path = action.field_path
field = fields.get(path)
if field is None:
message = f"{node_type} 没有字段 {path},只能修改字段定义中的路径"
keyword = next(
(str(item) for item in reversed(action.path) if isinstance(item, str)),
action.section,
)
if similar := search_fields(catalog, node_type, keyword, limit=3)["fields"]:
message += ";可能是:" + "、".join(
f"{item['label']}({item['path']})" for item in similar
)
raise ValueError(message)
if not field.editable:
raise ValueError(f"字段 {path}({field.label})不允许修改")
if error := field.value_error(action.value):
raise ValueError(f"字段 {path}({field.label}){error}")
+207
View File
@@ -0,0 +1,207 @@
{
"order": 2,
"key": "area-chart",
"type": "area-chart",
"name": "面积图",
"description": "折线下方填充面积,展示数值随时间的变化趋势并突出总量",
"tags": [
"charts",
"面积图",
"area-chart"
],
"renderer": "echarts",
"group": "charts",
"icon": "solar:graph-new-bold",
"sample_data": true,
"usage_notes": [
"默认图表数据仅用于演示,不是真实业务数据",
"通过顶层 dataId 关联已有数据源,当前后端尚未实现绑定操作"
],
"field_groups": [
"node-base",
"chart-title",
"chart-legend",
"chart-grid",
"chart-axis",
"chart-line-series",
{
"key": "area-fill",
"label": "面积",
"description": "折线下方填充区域",
"fields": [
{
"path": "props.option.series.0.areaStyle.color",
"label": "面积色",
"description": "折线下方填充区域的颜色,通常带透明度,例如 rgba(64, 158, 255, 0.25)",
"value_type": "color",
"aliases": [
"填充色",
"面积颜色",
"阴影颜色"
]
}
]
},
"chart-encode-xy"
],
"eventOptions": [
{
"label": "点击事件",
"value": "click"
},
{
"label": "双击事件",
"value": "dblclick"
},
{
"label": "鼠标进入事件",
"value": "mouseenter"
},
{
"label": "鼠标离开事件",
"value": "mouseleave"
},
{
"label": "鼠标按下事件",
"value": "mousedown"
},
{
"label": "鼠标抬起事件",
"value": "mouseup"
},
{
"label": "鼠标移动事件",
"value": "mousemove"
},
{
"label": "鼠标滚轮事件",
"value": "mousewheel"
},
{
"label": "生命周期事件",
"value": "vnodeMounted"
}
],
"schema": {
"type": "area-chart",
"name": "面积图",
"layout": {
"x": 0,
"y": 0,
"width": 400,
"height": 260
},
"props": {
"option": {
"legend": {
"top": 38,
"left": "center",
"itemWidth": 12,
"itemHeight": 8,
"show": true,
"textStyle": {
"color": "#cfd3dc"
}
},
"title": {
"text": "成交额趋势",
"top": 8,
"left": "center",
"textStyle": {
"color": "#e5eaf3",
"fontSize": 16
}
},
"tooltip": {
"trigger": "axis"
},
"dataset": {
"source": [
{
"date": "周一",
"revenue": 120
},
{
"date": "周二",
"revenue": 200
},
{
"date": "周三",
"revenue": 150
},
{
"date": "周四",
"revenue": 260
},
{
"date": "周五",
"revenue": 330
},
{
"date": "周六",
"revenue": 420
},
{
"date": "周日",
"revenue": 510
}
]
},
"grid": {
"top": 86,
"right": 24,
"bottom": 32,
"left": 48,
"containLabel": true
},
"xAxis": {
"type": "category",
"boundaryGap": false,
"axisLine": {
"lineStyle": {
"color": "#4c4d4f"
}
},
"axisLabel": {
"color": "#cfd3dc"
}
},
"yAxis": {
"type": "value",
"axisLabel": {
"color": "#cfd3dc"
},
"splitLine": {
"lineStyle": {
"color": "rgba(255, 255, 255, 0.08)"
}
}
},
"series": [
{
"name": "成交额",
"type": "line",
"smooth": true,
"symbolSize": 8,
"encode": {
"x": "date",
"y": "revenue"
},
"lineStyle": {
"width": 3,
"color": "#409eff"
},
"itemStyle": {
"color": "#409eff"
},
"areaStyle": {
"color": "rgba(64, 158, 255, 0.25)"
}
}
]
}
},
"style": null,
"events": []
}
}
+274
View File
@@ -0,0 +1,274 @@
{
"order": 0,
"key": "bar-chart",
"type": "bar-chart",
"name": "柱状图",
"description": "按类目对比数值大小,适合月度、地区、产品等分类数据的对比",
"tags": [
"charts",
"柱状图",
"bar-chart"
],
"renderer": "echarts",
"group": "charts",
"icon": "solar:chart-2-bold",
"sample_data": true,
"usage_notes": [
"默认图表数据仅用于演示,不是真实业务数据",
"通过顶层 dataId 关联已有数据源,当前后端尚未实现绑定操作"
],
"field_groups": [
"node-base",
"chart-title",
"chart-legend",
"chart-grid",
"chart-axis",
{
"key": "bar-series",
"label": "柱形",
"description": "柱子的颜色、宽度、圆角、系列名称和数值标签",
"fields": [
{
"path": "props.option.series.0.itemStyle.color",
"label": "柱颜色",
"description": "柱子的填充颜色",
"value_type": "color",
"aliases": [
"颜色",
"柱子颜色",
"主色"
]
},
{
"path": "props.option.series.0.name",
"label": "系列名称",
"description": "这组柱子的名称,显示在图例和鼠标悬停提示中;不是图表标题",
"value_type": "string",
"aliases": [
"图例名称",
"系列名",
"数据名称"
],
"panel": false
},
{
"path": "props.option.series.0.barWidth",
"label": "柱宽",
"description": "柱子宽度,百分比表示占每个类目宽度的比例,例如 45%",
"value_type": "string",
"aliases": [
"柱子宽度",
"柱子粗细"
],
"panel": false
},
{
"path": "props.option.series.0.itemStyle.borderRadius",
"label": "柱圆角",
"description": "柱子四个角的圆角半径,顺序为左上、右上、右下、左下",
"value_type": "array",
"item_type": "number",
"unit": "px",
"aliases": [
"圆角"
],
"panel": false
},
{
"path": "props.option.series.0.label.show",
"label": "数值标签",
"description": "是否在每根柱子上显示数值",
"value_type": "boolean",
"default": false,
"aliases": [
"显示数值",
"数据标签",
"标签"
],
"panel": false
},
{
"path": "props.option.series.0.label.position",
"label": "标签位置",
"description": "数值标签相对柱子的位置,开启数值标签后生效",
"value_type": "enum",
"default": "inside",
"options": [
{
"value": "top",
"label": "柱顶上方"
},
{
"value": "inside",
"label": "柱内居中"
},
{
"value": "insideTop",
"label": "柱内顶端"
},
{
"value": "insideBottom",
"label": "柱内底端"
}
],
"aliases": [
"数值位置"
],
"panel": false
}
]
},
"chart-encode-xy"
],
"eventOptions": [
{
"label": "点击事件",
"value": "click"
},
{
"label": "双击事件",
"value": "dblclick"
},
{
"label": "鼠标进入事件",
"value": "mouseenter"
},
{
"label": "鼠标离开事件",
"value": "mouseleave"
},
{
"label": "鼠标按下事件",
"value": "mousedown"
},
{
"label": "鼠标抬起事件",
"value": "mouseup"
},
{
"label": "鼠标移动事件",
"value": "mousemove"
},
{
"label": "鼠标滚轮事件",
"value": "mousewheel"
},
{
"label": "生命周期事件",
"value": "vnodeMounted"
}
],
"schema": {
"type": "bar-chart",
"name": "柱状图",
"layout": {
"x": 0,
"y": 0,
"width": 400,
"height": 260
},
"props": {
"option": {
"legend": {
"top": 38,
"left": "center",
"itemWidth": 12,
"itemHeight": 8,
"show": true,
"textStyle": {
"color": "#cfd3dc"
}
},
"title": {
"text": "月度销售额",
"top": 8,
"left": "center",
"textStyle": {
"color": "#e5eaf3",
"fontSize": 16
}
},
"tooltip": {},
"dataset": {
"source": [
{
"month": "一月",
"sales": 120
},
{
"month": "二月",
"sales": 200
},
{
"month": "三月",
"sales": 150
},
{
"month": "四月",
"sales": 80
},
{
"month": "五月",
"sales": 170
},
{
"month": "六月",
"sales": 240
}
]
},
"grid": {
"top": 86,
"right": 24,
"bottom": 32,
"left": 48,
"containLabel": true
},
"xAxis": {
"type": "category",
"axisLine": {
"lineStyle": {
"color": "#4c4d4f"
}
},
"axisLabel": {
"color": "#cfd3dc"
}
},
"yAxis": {
"type": "value",
"axisLabel": {
"color": "#cfd3dc"
},
"splitLine": {
"lineStyle": {
"color": "rgba(255, 255, 255, 0.08)"
}
}
},
"series": [
{
"name": "销售额",
"type": "bar",
"barWidth": "45%",
"encode": {
"x": "month",
"y": "sales"
},
"itemStyle": {
"color": "#409eff",
"borderRadius": [
4,
4,
0,
0
]
}
}
]
}
},
"style": null,
"events": []
}
}
+382
View File
@@ -0,0 +1,382 @@
{
"order": 11,
"key": "bar-line-chart",
"type": "bar-line-chart",
"name": "柱线混合图",
"description": "柱子展示数量、折线展示比率,左右两个纵轴分别对应两种数值",
"tags": [
"charts",
"柱线混合图",
"bar-line-chart",
"双轴",
"混合",
"增长率"
],
"renderer": "echarts",
"group": "charts",
"icon": "solar:chart-2-linear",
"sample_data": true,
"usage_notes": [
"默认图表数据仅用于演示,不是真实业务数据",
"通过顶层 dataId 关联已有数据源,当前后端尚未实现绑定操作",
"柱系列使用左轴,折线系列 yAxisIndex=1 使用右轴",
"两个系列 encode.x 需保持一致"
],
"field_groups": [
"node-base",
"chart-title",
"chart-legend",
"chart-grid",
"chart-palette",
{
"key": "bar-line-series",
"label": "系列",
"description": "柱系列和折线系列的颜色、名称和样式",
"fields": [
{
"path": "props.option.color.0",
"label": "柱颜色",
"description": "柱子的颜色",
"value_type": "color",
"span": 12,
"aliases": [
"颜色",
"主色"
]
},
{
"path": "props.option.color.1",
"label": "线颜色",
"description": "折线的颜色",
"value_type": "color",
"span": 12,
"aliases": [
"折线颜色"
]
},
{
"path": "props.option.series.1.smooth",
"label": "平滑曲线",
"description": "开启后折线为平滑曲线,关闭后为折角直线",
"value_type": "boolean",
"aliases": [
"曲线",
"平滑"
]
},
{
"path": "props.option.series.0.name",
"label": "柱系列名称",
"description": "柱子系列的名称,显示在图例和鼠标悬停提示中",
"value_type": "string",
"aliases": [
"柱子名称"
],
"panel": false
},
{
"path": "props.option.series.1.name",
"label": "线系列名称",
"description": "折线系列的名称,显示在图例和鼠标悬停提示中",
"value_type": "string",
"aliases": [
"折线名称"
],
"panel": false
},
{
"path": "props.option.series.0.barWidth",
"label": "柱宽",
"description": "柱子宽度,百分比表示占每个类目宽度的比例,例如 40%",
"value_type": "string",
"aliases": [
"柱子宽度"
],
"panel": false
}
]
},
{
"key": "bar-line-axis",
"label": "坐标轴",
"description": "左纵轴对应柱子数值,右纵轴对应折线数值",
"fields": [
{
"path": "props.option.yAxis.0.name",
"label": "左轴名称",
"description": "左侧纵轴(柱子数值)顶部显示的名称",
"value_type": "string",
"span": 12,
"aliases": [
"左轴标题"
]
},
{
"path": "props.option.yAxis.1.name",
"label": "右轴名称",
"description": "右侧纵轴(折线数值)顶部显示的名称",
"value_type": "string",
"span": 12,
"aliases": [
"右轴标题"
]
},
{
"path": "props.option.yAxis.1.axisLabel.formatter",
"label": "右轴格式",
"description": "右侧纵轴刻度的显示格式,{value} 代表刻度值,例如 {value}%",
"value_type": "string",
"aliases": [
"右轴单位"
],
"panel": false
},
{
"path": "props.option.xAxis.axisLabel.color",
"label": "X轴文字色",
"description": "横轴类目文字的颜色",
"value_type": "color",
"aliases": [
"横轴文字颜色"
],
"panel": false
}
]
},
{
"key": "bar-line-data",
"label": "数据映射",
"description": "柱线混合图从数据集(dataset.source)中取哪些列",
"fields": [
{
"path": "props.option.series.0.encode.x",
"label": "X字段",
"description": "横轴类目使用数据集中的哪一列;两个系列必须相同,修改时同时修改折线的X字段",
"value_type": "string",
"aliases": [
"横轴字段",
"类目字段"
]
},
{
"path": "props.option.series.1.encode.x",
"label": "折线X字段",
"description": "须与X字段保持一致,修改X字段时同步修改",
"value_type": "string",
"panel": false
},
{
"path": "props.option.series.0.encode.y",
"label": "柱数值字段",
"description": "柱子高度使用数据集中的哪一列",
"value_type": "string",
"aliases": [
"柱子字段"
]
},
{
"path": "props.option.series.1.encode.y",
"label": "线数值字段",
"description": "折线数值使用数据集中的哪一列",
"value_type": "string",
"aliases": [
"折线字段"
]
}
]
}
],
"eventOptions": [
{
"label": "点击事件",
"value": "click"
},
{
"label": "双击事件",
"value": "dblclick"
},
{
"label": "鼠标进入事件",
"value": "mouseenter"
},
{
"label": "鼠标离开事件",
"value": "mouseleave"
},
{
"label": "鼠标按下事件",
"value": "mousedown"
},
{
"label": "鼠标抬起事件",
"value": "mouseup"
},
{
"label": "鼠标移动事件",
"value": "mousemove"
},
{
"label": "鼠标滚轮事件",
"value": "mousewheel"
},
{
"label": "生命周期事件",
"value": "vnodeMounted"
}
],
"schema": {
"type": "bar-line-chart",
"name": "柱线混合图",
"layout": {
"x": 0,
"y": 0,
"width": 400,
"height": 260
},
"props": {
"option": {
"color": [
"#409eff",
"#e6a23c"
],
"legend": {
"top": 38,
"left": "center",
"itemWidth": 12,
"itemHeight": 8,
"show": true,
"textStyle": {
"color": "#cfd3dc"
}
},
"title": {
"text": "销售额与增长率",
"top": 8,
"left": "center",
"textStyle": {
"color": "#e5eaf3",
"fontSize": 16
}
},
"tooltip": {
"trigger": "axis"
},
"dataset": {
"source": [
{
"month": "一月",
"sales": 120,
"growth": 12.5
},
{
"month": "二月",
"sales": 200,
"growth": 18.2
},
{
"month": "三月",
"sales": 150,
"growth": -4.3
},
{
"month": "四月",
"sales": 80,
"growth": -20.1
},
{
"month": "五月",
"sales": 170,
"growth": 9.6
},
{
"month": "六月",
"sales": 240,
"growth": 21.4
}
]
},
"grid": {
"top": 86,
"right": 48,
"bottom": 32,
"left": 48,
"containLabel": true
},
"xAxis": {
"type": "category",
"axisLine": {
"lineStyle": {
"color": "#4c4d4f"
}
},
"axisLabel": {
"color": "#cfd3dc"
}
},
"yAxis": [
{
"type": "value",
"axisLabel": {
"color": "#cfd3dc"
},
"splitLine": {
"lineStyle": {
"color": "rgba(255, 255, 255, 0.08)"
}
},
"name": "销售额",
"nameTextStyle": {
"color": "#a3a6ad"
}
},
{
"type": "value",
"axisLabel": {
"color": "#cfd3dc",
"formatter": "{value}%"
},
"splitLine": {
"show": false
},
"name": "增长率",
"nameTextStyle": {
"color": "#a3a6ad"
}
}
],
"series": [
{
"name": "销售额",
"type": "bar",
"barWidth": "40%",
"encode": {
"x": "month",
"y": "sales"
},
"itemStyle": {
"borderRadius": [
4,
4,
0,
0
]
}
},
{
"name": "增长率",
"type": "line",
"yAxisIndex": 1,
"smooth": true,
"symbolSize": 6,
"encode": {
"x": "month",
"y": "growth"
}
}
]
}
},
"style": null,
"events": []
}
}
+566
View File
@@ -0,0 +1,566 @@
[
{
"key": "node-base",
"label": "基础",
"description": "所有组件共有的名称、位置和尺寸",
"fields": [
{
"path": "name",
"label": "组件名称",
"description": "组件在编辑器图层列表和属性面板标题栏中的名称,用于编辑时识别组件,不显示在大屏画面上;图表画面上显示的名字是标题",
"value_type": "string",
"aliases": [
"组件名",
"图层名",
"图层名称"
],
"panel": false
},
{
"path": "layout.x",
"label": "X",
"description": "组件左上角距画布左边缘的水平距离",
"value_type": "number",
"unit": "px",
"aliases": [
"横坐标",
"水平位置",
"左边距离"
],
"panel": false
},
{
"path": "layout.y",
"label": "Y",
"description": "组件左上角距画布上边缘的垂直距离",
"value_type": "number",
"unit": "px",
"aliases": [
"纵坐标",
"垂直位置",
"上边距离"
],
"panel": false
},
{
"path": "layout.width",
"label": "宽度",
"description": "组件的宽度",
"value_type": "number",
"minimum": 1,
"unit": "px",
"aliases": [
"宽",
"横向尺寸"
],
"panel": false
},
{
"path": "layout.height",
"label": "高度",
"description": "组件的高度",
"value_type": "number",
"minimum": 1,
"unit": "px",
"aliases": [
"高",
"纵向尺寸"
],
"panel": false
}
]
},
{
"key": "chart-title",
"label": "标题",
"description": "图表上方显示的标题文字及其颜色、字号、对齐",
"fields": [
{
"path": "props.option.title.text",
"label": "标题",
"description": "显示在图表上方的标题文字,是用户在大屏上看到的图表名字",
"value_type": "string",
"aliases": [
"名字",
"名称",
"图表名称",
"图表标题",
"标题文字"
]
},
{
"path": "props.option.title.textStyle.color",
"label": "标题色",
"description": "标题文字的颜色",
"value_type": "color",
"aliases": [
"标题颜色",
"标题文字颜色"
]
},
{
"path": "props.option.title.left",
"label": "对齐",
"description": "标题在组件内的水平位置",
"value_type": "enum",
"options": [
{
"value": "left",
"label": "左对齐"
},
{
"value": "center",
"label": "居中"
},
{
"value": "right",
"label": "右对齐"
}
],
"aliases": [
"标题对齐",
"标题位置"
]
},
{
"path": "props.option.title.textStyle.fontSize",
"label": "标题字号",
"description": "标题文字的大小",
"value_type": "number",
"minimum": 8,
"maximum": 72,
"unit": "px",
"aliases": [
"标题大小",
"标题字体大小"
],
"panel": false
},
{
"path": "props.option.title.show",
"label": "显示标题",
"description": "是否显示标题,关闭后整个标题隐藏",
"value_type": "boolean",
"default": true,
"aliases": [
"隐藏标题",
"去掉标题"
],
"panel": false
}
]
},
{
"key": "chart-legend",
"label": "图例",
"description": "标题下方列出各系列名称和颜色的图例",
"fields": [
{
"path": "props.option.legend.show",
"label": "图例显示",
"description": "是否显示图例;图例列出各系列的名称和颜色",
"value_type": "boolean",
"aliases": [
"图例",
"显示图例",
"隐藏图例"
]
},
{
"path": "props.option.legend.textStyle.color",
"label": "图例文字色",
"description": "图例中系列名称文字的颜色",
"value_type": "color",
"aliases": [
"图例颜色",
"图例文字颜色"
],
"panel": false
}
]
},
{
"key": "chart-grid",
"label": "边距",
"description": "绘图区(坐标轴所在区域)与组件四边的距离",
"fields": [
{
"path": "props.option.grid.top",
"label": "上边距",
"description": "绘图区距组件上边缘的距离;默认为标题和图例留出位置,调小可能与它们重叠",
"value_type": "number",
"minimum": 0,
"unit": "px",
"span": 12
},
{
"path": "props.option.grid.right",
"label": "右边距",
"description": "绘图区距组件右边缘的距离",
"value_type": "number",
"minimum": 0,
"unit": "px",
"span": 12
},
{
"path": "props.option.grid.bottom",
"label": "下边距",
"description": "绘图区距组件下边缘的距离,横轴文字显示在这里",
"value_type": "number",
"minimum": 0,
"unit": "px",
"span": 12
},
{
"path": "props.option.grid.left",
"label": "左边距",
"description": "绘图区距组件左边缘的距离,纵轴文字显示在这里",
"value_type": "number",
"minimum": 0,
"unit": "px",
"span": 12
}
]
},
{
"key": "chart-axis",
"label": "坐标轴",
"description": "直角坐标图表的横轴、纵轴和网格线颜色",
"fields": [
{
"path": "props.option.xAxis.axisLabel.color",
"label": "X轴文字色",
"description": "横轴刻度文字的颜色",
"value_type": "color",
"aliases": [
"横轴文字颜色",
"X轴标签颜色"
],
"panel": false
},
{
"path": "props.option.xAxis.axisLine.lineStyle.color",
"label": "X轴线颜色",
"description": "横轴轴线的颜色",
"value_type": "color",
"aliases": [
"横轴颜色",
"坐标轴颜色"
],
"panel": false
},
{
"path": "props.option.yAxis.axisLabel.color",
"label": "Y轴文字色",
"description": "纵轴刻度文字的颜色",
"value_type": "color",
"aliases": [
"纵轴文字颜色",
"Y轴标签颜色"
],
"panel": false
},
{
"path": "props.option.yAxis.splitLine.lineStyle.color",
"label": "网格线颜色",
"description": "绘图区内横向分隔线的颜色",
"value_type": "color",
"aliases": [
"分隔线颜色",
"背景线颜色"
],
"panel": false
}
]
},
{
"key": "chart-palette",
"label": "配色",
"description": "各系列或数据项按顺序依次取用的颜色列表",
"fields": [
{
"path": "props.option.color",
"label": "调色盘",
"description": "颜色列表,第 1 个系列或数据项用第 1 个颜色,依次类推;整体换一套配色时修改它,只改某一项时用对应的单项颜色字段",
"value_type": "array",
"item_type": "color",
"aliases": [
"配色",
"配色方案",
"整体颜色"
],
"panel": false
}
]
},
{
"key": "chart-encode-xy",
"label": "数据映射",
"description": "图表从数据集(dataset.source)中取哪一列作为横轴和纵轴",
"fields": [
{
"path": "props.option.series.0.encode.x",
"label": "X字段",
"description": "横轴使用数据集中的哪一列,必须是 dataset.source 中存在的字段名",
"value_type": "string",
"aliases": [
"横轴字段"
]
},
{
"path": "props.option.series.0.encode.y",
"label": "Y字段",
"description": "纵轴使用数据集中的哪一列,必须是 dataset.source 中存在的字段名",
"value_type": "string",
"aliases": [
"纵轴字段"
]
}
]
},
{
"key": "chart-encode-item",
"label": "数据映射",
"description": "图表从数据集(dataset.source)中取哪一列作为数据项名称和数值",
"fields": [
{
"path": "props.option.series.0.encode.itemName",
"label": "名称字段",
"description": "每个数据项(扇区、层级等)的名称使用数据集中的哪一列,必须是 dataset.source 中存在的字段名",
"value_type": "string",
"aliases": [
"类目字段",
"分类字段"
]
},
{
"path": "props.option.series.0.encode.value",
"label": "数值字段",
"description": "每个数据项的数值使用数据集中的哪一列,必须是 dataset.source 中存在的字段名",
"value_type": "string",
"aliases": [
"值字段"
]
}
]
},
{
"key": "chart-line-series",
"label": "线条",
"description": "折线的颜色、粗细、平滑度、数据点和数值标签",
"fields": [
{
"path": "props.option.series.0.lineStyle.color",
"label": "线颜色",
"description": "折线的颜色",
"value_type": "color",
"aliases": [
"颜色",
"折线颜色",
"主色"
]
},
{
"path": "props.option.series.0.name",
"label": "系列名称",
"description": "这条折线的名称,显示在图例和鼠标悬停提示中;不是图表标题",
"value_type": "string",
"aliases": [
"图例名称",
"系列名",
"数据名称"
],
"panel": false
},
{
"path": "props.option.series.0.smooth",
"label": "平滑曲线",
"description": "开启后折线为平滑曲线,关闭后为折角直线",
"value_type": "boolean",
"aliases": [
"曲线",
"平滑"
],
"panel": false
},
{
"path": "props.option.series.0.lineStyle.width",
"label": "线宽",
"description": "折线的粗细",
"value_type": "number",
"minimum": 1,
"maximum": 12,
"unit": "px",
"aliases": [
"线条粗细",
"线粗细"
],
"panel": false
},
{
"path": "props.option.series.0.itemStyle.color",
"label": "数据点颜色",
"description": "折线上每个数据点(拐点)的颜色",
"value_type": "color",
"aliases": [
"拐点颜色",
"圆点颜色"
],
"panel": false
},
{
"path": "props.option.series.0.symbolSize",
"label": "数据点大小",
"description": "折线上数据点(拐点)的直径",
"value_type": "number",
"minimum": 0,
"maximum": 30,
"unit": "px",
"aliases": [
"拐点大小",
"圆点大小"
],
"panel": false
},
{
"path": "props.option.series.0.label.show",
"label": "数值标签",
"description": "是否在每个数据点上方显示数值",
"value_type": "boolean",
"default": false,
"aliases": [
"显示数值",
"数据标签",
"标签"
],
"panel": false
}
]
},
{
"key": "chart-item-series",
"label": "数据项",
"description": "饼图、漏斗图等按数据项着色的图表的主色、系列名称和数据项标签",
"fields": [
{
"path": "props.option.color.0",
"label": "主色",
"description": "调色盘的第一个颜色,用于第一个数据项(扇区或层级);其余数据项依次取调色盘后面的颜色",
"value_type": "color",
"aliases": [
"颜色",
"第一个颜色"
]
},
{
"path": "props.option.series.0.name",
"label": "系列名称",
"description": "这组数据的名称,显示在鼠标悬停提示中;不是图表标题",
"value_type": "string",
"aliases": [
"系列名",
"数据名称"
],
"panel": false
},
{
"path": "props.option.series.0.label.show",
"label": "数据项标签",
"description": "是否在每个数据项旁显示名称标签",
"value_type": "boolean",
"default": true,
"aliases": [
"标签",
"显示标签",
"扇区标签"
],
"panel": false
},
{
"path": "props.option.series.0.label.color",
"label": "标签文字色",
"description": "数据项名称标签的文字颜色",
"value_type": "color",
"aliases": [
"标签颜色"
],
"panel": false
}
]
},
{
"key": "chart-gauge-value",
"label": "数值",
"description": "仪表类图表的量程、进度颜色和中心数值显示",
"fields": [
{
"path": "props.option.series.0.progress.itemStyle.color",
"label": "进度色",
"description": "进度弧的颜色",
"value_type": "color",
"aliases": [
"颜色",
"主色",
"进度条颜色"
]
},
{
"path": "props.option.series.0.min",
"label": "最小值",
"description": "量程起点,进度从这个值开始计算",
"value_type": "number",
"span": 12,
"aliases": [
"量程最小值",
"起始值"
]
},
{
"path": "props.option.series.0.max",
"label": "最大值",
"description": "量程终点,数值达到它时进度为满",
"value_type": "number",
"span": 12,
"aliases": [
"量程最大值",
"目标值",
"满值"
]
},
{
"path": "props.option.series.0.detail.formatter",
"label": "数值格式",
"description": "中心数值的显示格式,{value} 代表当前值,例如 {value}%",
"value_type": "string",
"aliases": [
"数值单位",
"显示格式"
],
"panel": false
},
{
"path": "props.option.series.0.detail.color",
"label": "数值颜色",
"description": "中心数值文字的颜色",
"value_type": "color",
"aliases": [
"数字颜色"
],
"panel": false
},
{
"path": "props.option.series.0.detail.fontSize",
"label": "数值字号",
"description": "中心数值文字的大小",
"value_type": "number",
"minimum": 8,
"maximum": 96,
"unit": "px",
"aliases": [
"数字大小"
],
"panel": false
}
]
}
]
+201
View File
@@ -0,0 +1,201 @@
{
"order": 8,
"key": "funnel-chart",
"type": "funnel-chart",
"name": "漏斗图",
"description": "展示流程各阶段的数量和逐级转化情况",
"tags": [
"charts",
"漏斗图",
"funnel-chart"
],
"renderer": "echarts",
"group": "charts",
"icon": "solar:filter-bold",
"sample_data": true,
"usage_notes": [
"默认图表数据仅用于演示,不是真实业务数据",
"通过顶层 dataId 关联已有数据源,当前后端尚未实现绑定操作"
],
"field_groups": [
"node-base",
"chart-title",
"chart-legend",
"chart-palette",
"chart-item-series",
{
"key": "funnel-shape",
"label": "漏斗",
"description": "漏斗的排序和层间距",
"fields": [
{
"path": "props.option.series.0.sort",
"label": "排序",
"description": "各层按数值排列的方向",
"value_type": "enum",
"options": [
{
"value": "descending",
"label": "从大到小(正漏斗)"
},
{
"value": "ascending",
"label": "从小到大(倒漏斗)"
},
{
"value": "none",
"label": "按数据顺序"
}
],
"aliases": [
"方向",
"顺序"
],
"panel": false
},
{
"path": "props.option.series.0.gap",
"label": "层间距",
"description": "相邻两层之间的间隔",
"value_type": "number",
"minimum": 0,
"maximum": 40,
"unit": "px",
"aliases": [
"间隔",
"间距"
],
"panel": false
}
]
},
"chart-encode-item"
],
"eventOptions": [
{
"label": "点击事件",
"value": "click"
},
{
"label": "双击事件",
"value": "dblclick"
},
{
"label": "鼠标进入事件",
"value": "mouseenter"
},
{
"label": "鼠标离开事件",
"value": "mouseleave"
},
{
"label": "鼠标按下事件",
"value": "mousedown"
},
{
"label": "鼠标抬起事件",
"value": "mouseup"
},
{
"label": "鼠标移动事件",
"value": "mousemove"
},
{
"label": "鼠标滚轮事件",
"value": "mousewheel"
},
{
"label": "生命周期事件",
"value": "vnodeMounted"
}
],
"schema": {
"type": "funnel-chart",
"name": "漏斗图",
"layout": {
"x": 0,
"y": 0,
"width": 400,
"height": 280
},
"props": {
"option": {
"color": [
"#409eff",
"#67c23a",
"#e6a23c",
"#f56c6c",
"#909399"
],
"legend": {
"top": 38,
"left": "center",
"itemWidth": 12,
"itemHeight": 8,
"show": true,
"textStyle": {
"color": "#cfd3dc"
}
},
"title": {
"text": "转化漏斗",
"top": 8,
"left": "center",
"textStyle": {
"color": "#e5eaf3",
"fontSize": 16
}
},
"tooltip": {
"trigger": "item"
},
"dataset": {
"source": [
{
"stage": "访问",
"value": 100
},
{
"stage": "咨询",
"value": 72
},
{
"stage": "下单",
"value": 48
},
{
"stage": "成交",
"value": 26
}
]
},
"series": [
{
"name": "转化",
"type": "funnel",
"top": 72,
"bottom": 16,
"left": "12%",
"width": "76%",
"minSize": "16%",
"maxSize": "100%",
"sort": "descending",
"gap": 4,
"label": {
"color": "#cfd3dc"
},
"itemStyle": {
"borderColor": "transparent"
},
"encode": {
"itemName": "stage",
"value": "value"
}
}
]
}
},
"style": null,
"events": []
}
}
+234
View File
@@ -0,0 +1,234 @@
{
"order": 9,
"key": "gauge-chart",
"type": "gauge-chart",
"name": "仪表盘",
"description": "用指针和进度弧展示单个指标的当前值或完成率",
"tags": [
"charts",
"仪表盘",
"gauge-chart"
],
"renderer": "echarts",
"group": "charts",
"icon": "solar:speedometer-middle-bold",
"sample_data": true,
"usage_notes": [
"默认图表数据仅用于演示,不是真实业务数据",
"通过顶层 dataId 关联已有数据源,当前后端尚未实现绑定操作"
],
"field_groups": [
"node-base",
"chart-title",
"chart-gauge-value",
{
"key": "gauge-dial",
"label": "表盘",
"description": "仪表盘的指针、刻度和表盘宽度",
"fields": [
{
"path": "props.option.series.0.pointer.show",
"label": "显示指针",
"description": "是否显示指针",
"value_type": "boolean",
"default": true,
"aliases": [
"指针"
],
"panel": false
},
{
"path": "props.option.series.0.pointer.itemStyle.color",
"label": "指针颜色",
"description": "指针的颜色",
"value_type": "color",
"panel": false
},
{
"path": "props.option.series.0.axisLabel.color",
"label": "刻度文字色",
"description": "表盘刻度数字的颜色",
"value_type": "color",
"aliases": [
"刻度颜色"
],
"panel": false
},
{
"path": "props.option.series.0.axisLine.lineStyle.width",
"label": "表盘宽度",
"description": "表盘底弧的粗细",
"value_type": "number",
"minimum": 1,
"maximum": 60,
"unit": "px",
"aliases": [
"底弧宽度"
],
"panel": false
},
{
"path": "props.option.series.0.progress.width",
"label": "进度宽度",
"description": "进度弧的粗细",
"value_type": "number",
"minimum": 1,
"maximum": 60,
"unit": "px",
"aliases": [
"进度条宽度"
],
"panel": false
}
]
},
"chart-encode-item"
],
"eventOptions": [
{
"label": "点击事件",
"value": "click"
},
{
"label": "双击事件",
"value": "dblclick"
},
{
"label": "鼠标进入事件",
"value": "mouseenter"
},
{
"label": "鼠标离开事件",
"value": "mouseleave"
},
{
"label": "鼠标按下事件",
"value": "mousedown"
},
{
"label": "鼠标抬起事件",
"value": "mouseup"
},
{
"label": "鼠标移动事件",
"value": "mousemove"
},
{
"label": "鼠标滚轮事件",
"value": "mousewheel"
},
{
"label": "生命周期事件",
"value": "vnodeMounted"
}
],
"schema": {
"type": "gauge-chart",
"name": "仪表盘",
"layout": {
"x": 0,
"y": 0,
"width": 320,
"height": 280
},
"props": {
"option": {
"title": {
"text": "销售目标完成率",
"top": 8,
"left": "center",
"textStyle": {
"color": "#e5eaf3",
"fontSize": 16
}
},
"tooltip": {
"trigger": "item"
},
"dataset": {
"source": [
{
"name": "销售目标完成率",
"value": 72
}
]
},
"series": [
{
"type": "gauge",
"min": 0,
"max": 100,
"startAngle": 210,
"endAngle": -30,
"center": [
"50%",
"58%"
],
"radius": "80%",
"progress": {
"show": true,
"width": 12,
"itemStyle": {
"color": "#409eff"
}
},
"axisLine": {
"lineStyle": {
"width": 12,
"color": [
[
1,
"rgba(255, 255, 255, 0.1)"
]
]
}
},
"axisTick": {
"show": false
},
"splitLine": {
"length": 8,
"lineStyle": {
"width": 1,
"color": "#4c4d4f"
}
},
"axisLabel": {
"color": "#cfd3dc",
"distance": 16
},
"pointer": {
"itemStyle": {
"color": "#e5eaf3"
}
},
"title": {
"offsetCenter": [
0,
"72%"
],
"color": "#cfd3dc",
"fontSize": 13
},
"detail": {
"valueAnimation": true,
"offsetCenter": [
0,
"46%"
],
"color": "#e5eaf3",
"fontSize": 28,
"formatter": "{value}%"
},
"encode": {
"itemName": "name",
"value": "value"
}
}
]
}
},
"style": null,
"events": []
}
}
+320
View File
@@ -0,0 +1,320 @@
{
"order": 1,
"key": "hbar-chart",
"type": "hbar-chart",
"name": "条形图",
"description": "横向条形对比各类目数值,类目名称较长或类目较多时比柱状图更易读",
"tags": [
"charts",
"条形图",
"hbar-chart"
],
"renderer": "echarts",
"group": "charts",
"icon": "solar:align-left-bold",
"sample_data": true,
"usage_notes": [
"默认图表数据仅用于演示,不是真实业务数据",
"通过顶层 dataId 关联已有数据源,当前后端尚未实现绑定操作"
],
"field_groups": [
"node-base",
"chart-title",
"chart-legend",
"chart-grid",
{
"key": "hbar-axis",
"label": "坐标轴",
"description": "条形图的横轴为数值轴、纵轴为类目轴",
"fields": [
{
"path": "props.option.xAxis.axisLabel.color",
"label": "X轴文字色",
"description": "横轴(数值)刻度文字的颜色",
"value_type": "color",
"aliases": [
"横轴文字颜色",
"数值轴文字颜色"
],
"panel": false
},
{
"path": "props.option.xAxis.splitLine.lineStyle.color",
"label": "网格线颜色",
"description": "绘图区内纵向分隔线的颜色",
"value_type": "color",
"aliases": [
"分隔线颜色",
"背景线颜色"
],
"panel": false
},
{
"path": "props.option.yAxis.axisLabel.color",
"label": "Y轴文字色",
"description": "纵轴类目名称文字的颜色",
"value_type": "color",
"aliases": [
"纵轴文字颜色",
"类目文字颜色"
],
"panel": false
},
{
"path": "props.option.yAxis.axisLine.lineStyle.color",
"label": "Y轴线颜色",
"description": "纵轴轴线的颜色",
"value_type": "color",
"aliases": [
"纵轴颜色",
"坐标轴颜色"
],
"panel": false
}
]
},
{
"key": "hbar-series",
"label": "条形",
"description": "条形的颜色、粗细、圆角、系列名称和数值标签",
"fields": [
{
"path": "props.option.series.0.itemStyle.color",
"label": "条颜色",
"description": "条形的填充颜色",
"value_type": "color",
"aliases": [
"颜色",
"条形颜色",
"主色"
]
},
{
"path": "props.option.series.0.name",
"label": "系列名称",
"description": "这组条形的名称,显示在图例和鼠标悬停提示中;不是图表标题",
"value_type": "string",
"aliases": [
"图例名称",
"系列名",
"数据名称"
],
"panel": false
},
{
"path": "props.option.series.0.barWidth",
"label": "条宽",
"description": "条形粗细,百分比表示占每个类目高度的比例,例如 45%",
"value_type": "string",
"aliases": [
"条形宽度",
"条形粗细"
],
"panel": false
},
{
"path": "props.option.series.0.itemStyle.borderRadius",
"label": "条圆角",
"description": "条形四个角的圆角半径,顺序为左上、右上、右下、左下,默认只有右端圆角",
"value_type": "array",
"item_type": "number",
"unit": "px",
"aliases": [
"圆角"
],
"panel": false
},
{
"path": "props.option.series.0.label.show",
"label": "数值标签",
"description": "是否在每个条形上显示数值",
"value_type": "boolean",
"default": false,
"aliases": [
"显示数值",
"数据标签",
"标签"
],
"panel": false
},
{
"path": "props.option.series.0.label.position",
"label": "标签位置",
"description": "数值标签相对条形的位置,开启数值标签后生效",
"value_type": "enum",
"default": "inside",
"options": [
{
"value": "right",
"label": "条形右侧"
},
{
"value": "inside",
"label": "条内居中"
},
{
"value": "insideRight",
"label": "条内右端"
},
{
"value": "insideLeft",
"label": "条内左端"
}
],
"aliases": [
"数值位置"
],
"panel": false
}
]
},
"chart-encode-xy"
],
"eventOptions": [
{
"label": "点击事件",
"value": "click"
},
{
"label": "双击事件",
"value": "dblclick"
},
{
"label": "鼠标进入事件",
"value": "mouseenter"
},
{
"label": "鼠标离开事件",
"value": "mouseleave"
},
{
"label": "鼠标按下事件",
"value": "mousedown"
},
{
"label": "鼠标抬起事件",
"value": "mouseup"
},
{
"label": "鼠标移动事件",
"value": "mousemove"
},
{
"label": "鼠标滚轮事件",
"value": "mousewheel"
},
{
"label": "生命周期事件",
"value": "vnodeMounted"
}
],
"schema": {
"type": "hbar-chart",
"name": "条形图",
"layout": {
"x": 0,
"y": 0,
"width": 400,
"height": 260
},
"props": {
"option": {
"legend": {
"top": 38,
"left": "center",
"itemWidth": 12,
"itemHeight": 8,
"show": true,
"textStyle": {
"color": "#cfd3dc"
}
},
"title": {
"text": "地区销售额",
"top": 8,
"left": "center",
"textStyle": {
"color": "#e5eaf3",
"fontSize": 16
}
},
"tooltip": {},
"dataset": {
"source": [
{
"region": "华东",
"sales": 320
},
{
"region": "华南",
"sales": 240
},
{
"region": "华北",
"sales": 210
},
{
"region": "西南",
"sales": 180
},
{
"region": "西北",
"sales": 130
}
]
},
"grid": {
"top": 86,
"right": 24,
"bottom": 24,
"left": 48,
"containLabel": true
},
"xAxis": {
"type": "value",
"axisLabel": {
"color": "#cfd3dc"
},
"splitLine": {
"lineStyle": {
"color": "rgba(255, 255, 255, 0.08)"
}
}
},
"yAxis": {
"type": "category",
"axisLine": {
"lineStyle": {
"color": "#4c4d4f"
}
},
"axisLabel": {
"color": "#cfd3dc"
}
},
"series": [
{
"name": "销售额",
"type": "bar",
"barWidth": "45%",
"encode": {
"x": "sales",
"y": "region"
},
"itemStyle": {
"color": "#409eff",
"borderRadius": [
0,
4,
4,
0
]
}
}
]
}
},
"style": null,
"events": []
}
}
+516
View File
@@ -0,0 +1,516 @@
{
"order": 14,
"key": "heatmap-chart",
"type": "heatmap-chart",
"name": "热力图",
"description": "用格子颜色深浅展示两个类目维度交叉处的数值大小",
"tags": [
"charts",
"热力图",
"heatmap-chart",
"热力",
"分布",
"时段"
],
"renderer": "echarts",
"group": "charts",
"icon": "solar:widget-bold",
"sample_data": true,
"usage_notes": [
"默认图表数据仅用于演示,不是真实业务数据",
"通过顶层 dataId 关联已有数据源,当前后端尚未实现绑定操作",
"数据源每行一个格子,三列分别对应 X、Y、数值,数值列需放在最后一列(visualMap 默认映射最后一个维度)",
"visualMap.min / max 需按实际数据范围调整",
"label.formatter 用 {@字段名} 引用数值列,修改数值字段时需同步修改"
],
"field_groups": [
"node-base",
"chart-title",
"chart-grid",
{
"key": "heatmap-axis",
"label": "坐标轴",
"description": "热力图的横轴和纵轴都是类目轴",
"fields": [
{
"path": "props.option.xAxis.axisLabel.color",
"label": "X轴文字色",
"description": "横轴类目文字的颜色",
"value_type": "color",
"aliases": [
"横轴文字颜色"
],
"panel": false
},
{
"path": "props.option.yAxis.axisLabel.color",
"label": "Y轴文字色",
"description": "纵轴类目文字的颜色",
"value_type": "color",
"aliases": [
"纵轴文字颜色"
],
"panel": false
}
]
},
{
"key": "heatmap-visual",
"label": "色阶",
"description": "数值到格子颜色的映射,数值越大颜色越接近高值颜色",
"fields": [
{
"path": "props.option.visualMap.min",
"label": "最小值",
"description": "色阶的最小值,需按实际数据范围调整",
"value_type": "number",
"span": 12
},
{
"path": "props.option.visualMap.max",
"label": "最大值",
"description": "色阶的最大值,需按实际数据范围调整",
"value_type": "number",
"span": 12
},
{
"path": "props.option.visualMap.inRange.color.0",
"label": "低值颜色",
"description": "最小值对应的格子颜色",
"value_type": "color",
"span": 12
},
{
"path": "props.option.visualMap.inRange.color.4",
"label": "高值颜色",
"description": "最大值对应的格子颜色",
"value_type": "color",
"span": 12
},
{
"path": "props.option.visualMap.inRange.color",
"label": "色阶颜色",
"description": "从低到高的 5 个颜色,整体换配色时修改它",
"value_type": "array",
"item_type": "color",
"aliases": [
"配色",
"渐变色"
],
"panel": false
}
]
},
{
"key": "heatmap-label",
"label": "数值标签",
"description": "格子内显示的数值",
"fields": [
{
"path": "props.option.series.0.label.show",
"label": "显示数值",
"description": "是否在每个格子内显示数值",
"value_type": "boolean",
"aliases": [
"数值标签",
"标签"
]
},
{
"path": "props.option.series.0.label.formatter",
"label": "数值格式",
"description": "格子内数值的显示格式,用 {@字段名} 引用数值列,例如 {@value};修改数值字段时需同步修改",
"value_type": "string",
"aliases": [
"标签格式"
]
},
{
"path": "props.option.series.0.label.color",
"label": "数值文字色",
"description": "格子内数值文字的颜色",
"value_type": "color",
"aliases": [
"标签颜色"
],
"panel": false
}
]
},
"chart-encode-xy",
{
"key": "heatmap-value",
"label": "数值映射",
"description": "格子颜色使用的数值列",
"fields": [
{
"path": "props.option.series.0.encode.value",
"label": "数值字段",
"description": "格子数值使用数据集中的哪一列,必须是 dataset.source 中的最后一列;修改后同步修改数值格式",
"value_type": "string",
"aliases": [
"值字段"
]
}
]
}
],
"eventOptions": [
{
"label": "点击事件",
"value": "click"
},
{
"label": "双击事件",
"value": "dblclick"
},
{
"label": "鼠标进入事件",
"value": "mouseenter"
},
{
"label": "鼠标离开事件",
"value": "mouseleave"
},
{
"label": "鼠标按下事件",
"value": "mousedown"
},
{
"label": "鼠标抬起事件",
"value": "mouseup"
},
{
"label": "鼠标移动事件",
"value": "mousemove"
},
{
"label": "鼠标滚轮事件",
"value": "mousewheel"
},
{
"label": "生命周期事件",
"value": "vnodeMounted"
}
],
"schema": {
"type": "heatmap-chart",
"name": "热力图",
"layout": {
"x": 0,
"y": 0,
"width": 480,
"height": 300
},
"props": {
"option": {
"title": {
"text": "访问时段分布",
"top": 8,
"left": "center",
"textStyle": {
"color": "#e5eaf3",
"fontSize": 16
}
},
"tooltip": {
"position": "top"
},
"dataset": {
"source": [
{
"slot": "0-4点",
"day": "周一",
"value": 8
},
{
"slot": "4-8点",
"day": "周一",
"value": 7
},
{
"slot": "8-12点",
"day": "周一",
"value": 46
},
{
"slot": "12-16点",
"day": "周一",
"value": 62
},
{
"slot": "16-20点",
"day": "周一",
"value": 76
},
{
"slot": "20-24点",
"day": "周一",
"value": 84
},
{
"slot": "0-4点",
"day": "周二",
"value": 15
},
{
"slot": "4-8点",
"day": "周二",
"value": 14
},
{
"slot": "8-12点",
"day": "周二",
"value": 42
},
{
"slot": "12-16点",
"day": "周二",
"value": 58
},
{
"slot": "16-20点",
"day": "周二",
"value": 72
},
{
"slot": "20-24点",
"day": "周二",
"value": 80
},
{
"slot": "0-4点",
"day": "周三",
"value": 11
},
{
"slot": "4-8点",
"day": "周三",
"value": 10
},
{
"slot": "8-12点",
"day": "周三",
"value": 49
},
{
"slot": "12-16点",
"day": "周三",
"value": 65
},
{
"slot": "16-20点",
"day": "周三",
"value": 68
},
{
"slot": "20-24点",
"day": "周三",
"value": 76
},
{
"slot": "0-4点",
"day": "周四",
"value": 18
},
{
"slot": "4-8点",
"day": "周四",
"value": 6
},
{
"slot": "8-12点",
"day": "周四",
"value": 45
},
{
"slot": "12-16点",
"day": "周四",
"value": 61
},
{
"slot": "16-20点",
"day": "周四",
"value": 75
},
{
"slot": "20-24点",
"day": "周四",
"value": 83
},
{
"slot": "0-4点",
"day": "周五",
"value": 14
},
{
"slot": "4-8点",
"day": "周五",
"value": 13
},
{
"slot": "8-12点",
"day": "周五",
"value": 52
},
{
"slot": "12-16点",
"day": "周五",
"value": 57
},
{
"slot": "16-20点",
"day": "周五",
"value": 71
},
{
"slot": "20-24点",
"day": "周五",
"value": 79
},
{
"slot": "0-4点",
"day": "周六",
"value": 12
},
{
"slot": "4-8点",
"day": "周六",
"value": 11
},
{
"slot": "8-12点",
"day": "周六",
"value": 61
},
{
"slot": "12-16点",
"day": "周六",
"value": 81
},
{
"slot": "16-20点",
"day": "周六",
"value": 98
},
{
"slot": "20-24点",
"day": "周六",
"value": 97
},
{
"slot": "0-4点",
"day": "周日",
"value": 19
},
{
"slot": "4-8点",
"day": "周日",
"value": 7
},
{
"slot": "8-12点",
"day": "周日",
"value": 57
},
{
"slot": "12-16点",
"day": "周日",
"value": 77
},
{
"slot": "16-20点",
"day": "周日",
"value": 94
},
{
"slot": "20-24点",
"day": "周日",
"value": 104
}
]
},
"grid": {
"top": 48,
"right": 24,
"bottom": 64,
"left": 48,
"containLabel": true
},
"xAxis": {
"type": "category",
"axisLine": {
"lineStyle": {
"color": "#4c4d4f"
}
},
"axisLabel": {
"color": "#cfd3dc"
},
"splitArea": {
"show": true
}
},
"yAxis": {
"type": "category",
"axisLine": {
"lineStyle": {
"color": "#4c4d4f"
}
},
"axisLabel": {
"color": "#cfd3dc"
},
"inverse": true,
"splitArea": {
"show": true
}
},
"visualMap": {
"min": 0,
"max": 120,
"calculable": true,
"orient": "horizontal",
"left": "center",
"bottom": 4,
"itemHeight": 120,
"textStyle": {
"color": "#cfd3dc"
},
"inRange": {
"color": [
"#1d2b44",
"#337ecc",
"#79bbff",
"#e6a23c",
"#f56c6c"
]
}
},
"series": [
{
"name": "访问量",
"type": "heatmap",
"label": {
"show": true,
"formatter": "{@value}",
"color": "#e5eaf3",
"fontSize": 11
},
"itemStyle": {
"borderColor": "#141414",
"borderWidth": 2
},
"encode": {
"x": "slot",
"y": "day",
"value": "value"
}
}
]
}
},
"style": null,
"events": []
}
}
+186
View File
@@ -0,0 +1,186 @@
{
"order": 3,
"key": "line-chart",
"type": "line-chart",
"name": "折线图",
"description": "展示数值随时间或有序类目的变化趋势",
"tags": [
"charts",
"折线图",
"line-chart"
],
"renderer": "echarts",
"group": "charts",
"icon": "solar:graph-up-bold",
"sample_data": true,
"usage_notes": [
"默认图表数据仅用于演示,不是真实业务数据",
"通过顶层 dataId 关联已有数据源,当前后端尚未实现绑定操作"
],
"field_groups": [
"node-base",
"chart-title",
"chart-legend",
"chart-grid",
"chart-axis",
"chart-line-series",
"chart-encode-xy"
],
"eventOptions": [
{
"label": "点击事件",
"value": "click"
},
{
"label": "双击事件",
"value": "dblclick"
},
{
"label": "鼠标进入事件",
"value": "mouseenter"
},
{
"label": "鼠标离开事件",
"value": "mouseleave"
},
{
"label": "鼠标按下事件",
"value": "mousedown"
},
{
"label": "鼠标抬起事件",
"value": "mouseup"
},
{
"label": "鼠标移动事件",
"value": "mousemove"
},
{
"label": "鼠标滚轮事件",
"value": "mousewheel"
},
{
"label": "生命周期事件",
"value": "vnodeMounted"
}
],
"schema": {
"type": "line-chart",
"name": "折线图",
"layout": {
"x": 0,
"y": 0,
"width": 400,
"height": 260
},
"props": {
"option": {
"legend": {
"top": 38,
"left": "center",
"itemWidth": 12,
"itemHeight": 8,
"show": true,
"textStyle": {
"color": "#cfd3dc"
}
},
"title": {
"text": "访问量趋势",
"top": 8,
"left": "center",
"textStyle": {
"color": "#e5eaf3",
"fontSize": 16
}
},
"tooltip": {
"trigger": "axis"
},
"dataset": {
"source": [
{
"date": "周一",
"visits": 820
},
{
"date": "周二",
"visits": 932
},
{
"date": "周三",
"visits": 901
},
{
"date": "周四",
"visits": 934
},
{
"date": "周五",
"visits": 1290
},
{
"date": "周六",
"visits": 1330
},
{
"date": "周日",
"visits": 1320
}
]
},
"grid": {
"top": 86,
"right": 24,
"bottom": 32,
"left": 48,
"containLabel": true
},
"xAxis": {
"type": "category",
"boundaryGap": false,
"axisLine": {
"lineStyle": {
"color": "#4c4d4f"
}
},
"axisLabel": {
"color": "#cfd3dc"
}
},
"yAxis": {
"type": "value",
"axisLabel": {
"color": "#cfd3dc"
},
"splitLine": {
"lineStyle": {
"color": "rgba(255, 255, 255, 0.08)"
}
}
},
"series": [
{
"name": "访问量",
"type": "line",
"smooth": true,
"symbolSize": 8,
"encode": {
"x": "date",
"y": "visits"
},
"lineStyle": {
"width": 3,
"color": "#409eff"
},
"itemStyle": {
"color": "#409eff"
}
}
]
}
},
"style": null,
"events": []
}
}
@@ -0,0 +1,272 @@
{
"order": 13,
"key": "pictorial-bar-chart",
"type": "pictorial-bar-chart",
"name": "象形柱图",
"description": "用重复的图形堆叠成柱子对比各类目数值,视觉上更形象",
"tags": [
"charts",
"象形柱图",
"pictorial-bar-chart",
"象形",
"分段",
"装饰"
],
"renderer": "echarts",
"group": "charts",
"icon": "solar:battery-charge-bold",
"sample_data": true,
"usage_notes": [
"默认图表数据仅用于演示,不是真实业务数据",
"通过顶层 dataId 关联已有数据源,当前后端尚未实现绑定操作"
],
"field_groups": [
"node-base",
"chart-title",
"chart-grid",
"chart-axis",
{
"key": "pictorial-series",
"label": "图形",
"description": "组成柱子的图形样式",
"fields": [
{
"path": "props.option.series.0.itemStyle.color",
"label": "图形颜色",
"description": "图形的颜色",
"value_type": "color",
"aliases": [
"颜色",
"主色"
]
},
{
"path": "props.option.series.0.symbol",
"label": "图形",
"description": "组成柱子的图形形状",
"value_type": "enum",
"options": [
{
"value": "rect",
"label": "矩形"
},
{
"value": "roundRect",
"label": "圆角矩形"
},
{
"value": "circle",
"label": "圆形"
},
{
"value": "triangle",
"label": "三角形"
},
{
"value": "diamond",
"label": "菱形"
}
],
"aliases": [
"形状"
]
},
{
"path": "props.option.series.0.symbolSize.0",
"label": "图形宽度",
"description": "单个图形的宽度",
"value_type": "number",
"span": 12,
"minimum": 1,
"unit": "px"
},
{
"path": "props.option.series.0.symbolSize.1",
"label": "图形高度",
"description": "单个图形的高度",
"value_type": "number",
"span": 12,
"minimum": 1,
"unit": "px"
},
{
"path": "props.option.series.0.symbolMargin",
"label": "图形间距",
"description": "重复图形之间的间隔",
"value_type": "number",
"span": 12,
"minimum": 0,
"unit": "px",
"aliases": [
"间距"
]
},
{
"path": "props.option.series.0.symbolRepeat",
"label": "重复图形",
"description": "开启后用多个小图形重复堆叠成柱子,关闭后用一个拉伸的图形",
"value_type": "boolean",
"span": 12,
"aliases": [
"重复"
]
},
{
"path": "props.option.series.0.name",
"label": "系列名称",
"description": "这组柱子的名称,显示在鼠标悬停提示中;不是图表标题",
"value_type": "string",
"aliases": [
"系列名",
"数据名称"
],
"panel": false
}
]
},
"chart-encode-xy"
],
"eventOptions": [
{
"label": "点击事件",
"value": "click"
},
{
"label": "双击事件",
"value": "dblclick"
},
{
"label": "鼠标进入事件",
"value": "mouseenter"
},
{
"label": "鼠标离开事件",
"value": "mouseleave"
},
{
"label": "鼠标按下事件",
"value": "mousedown"
},
{
"label": "鼠标抬起事件",
"value": "mouseup"
},
{
"label": "鼠标移动事件",
"value": "mousemove"
},
{
"label": "鼠标滚轮事件",
"value": "mousewheel"
},
{
"label": "生命周期事件",
"value": "vnodeMounted"
}
],
"schema": {
"type": "pictorial-bar-chart",
"name": "象形柱图",
"layout": {
"x": 0,
"y": 0,
"width": 400,
"height": 260
},
"props": {
"option": {
"title": {
"text": "各仓库库存量",
"top": 8,
"left": "center",
"textStyle": {
"color": "#e5eaf3",
"fontSize": 16
}
},
"tooltip": {
"trigger": "axis",
"axisPointer": {
"type": "none"
}
},
"dataset": {
"source": [
{
"warehouse": "上海仓",
"stock": 86
},
{
"warehouse": "广州仓",
"stock": 72
},
{
"warehouse": "北京仓",
"stock": 64
},
{
"warehouse": "成都仓",
"stock": 45
},
{
"warehouse": "武汉仓",
"stock": 38
}
]
},
"grid": {
"top": 48,
"right": 24,
"bottom": 32,
"left": 48,
"containLabel": true
},
"xAxis": {
"type": "category",
"axisLine": {
"lineStyle": {
"color": "#4c4d4f"
}
},
"axisLabel": {
"color": "#cfd3dc"
}
},
"yAxis": {
"type": "value",
"axisLabel": {
"color": "#cfd3dc"
},
"splitLine": {
"lineStyle": {
"color": "rgba(255, 255, 255, 0.08)"
}
}
},
"series": [
{
"name": "库存",
"type": "pictorialBar",
"symbol": "rect",
"symbolRepeat": true,
"symbolSize": [
24,
6
],
"symbolMargin": 2,
"symbolClip": true,
"itemStyle": {
"color": "#409eff"
},
"encode": {
"x": "warehouse",
"y": "stock"
}
}
]
}
},
"style": null,
"events": []
}
}
+154
View File
@@ -0,0 +1,154 @@
{
"order": 4,
"key": "pie-chart",
"type": "pie-chart",
"name": "饼图",
"description": "展示各部分占整体的比例,适合类目不多的占比数据",
"tags": [
"charts",
"饼图",
"pie-chart"
],
"renderer": "echarts",
"group": "charts",
"icon": "solar:pie-chart-2-bold",
"sample_data": true,
"usage_notes": [
"默认图表数据仅用于演示,不是真实业务数据",
"通过顶层 dataId 关联已有数据源,当前后端尚未实现绑定操作"
],
"field_groups": [
"node-base",
"chart-title",
"chart-legend",
"chart-palette",
"chart-item-series",
"chart-encode-item"
],
"eventOptions": [
{
"label": "点击事件",
"value": "click"
},
{
"label": "双击事件",
"value": "dblclick"
},
{
"label": "鼠标进入事件",
"value": "mouseenter"
},
{
"label": "鼠标离开事件",
"value": "mouseleave"
},
{
"label": "鼠标按下事件",
"value": "mousedown"
},
{
"label": "鼠标抬起事件",
"value": "mouseup"
},
{
"label": "鼠标移动事件",
"value": "mousemove"
},
{
"label": "鼠标滚轮事件",
"value": "mousewheel"
},
{
"label": "生命周期事件",
"value": "vnodeMounted"
}
],
"schema": {
"type": "pie-chart",
"name": "饼图",
"layout": {
"x": 0,
"y": 0,
"width": 400,
"height": 260
},
"props": {
"option": {
"color": [
"#409eff",
"#67c23a",
"#e6a23c",
"#f56c6c",
"#909399"
],
"legend": {
"top": 38,
"left": "center",
"itemWidth": 12,
"itemHeight": 8,
"show": true,
"textStyle": {
"color": "#cfd3dc"
}
},
"title": {
"text": "访问来源占比",
"top": 8,
"left": "center",
"textStyle": {
"color": "#e5eaf3",
"fontSize": 16
}
},
"tooltip": {
"trigger": "item"
},
"dataset": {
"source": [
{
"channel": "搜索引擎",
"value": 1048
},
{
"channel": "直接访问",
"value": 735
},
{
"channel": "社交媒体",
"value": 484
},
{
"channel": "广告投放",
"value": 300
}
]
},
"series": [
{
"name": "访问来源",
"type": "pie",
"center": [
"50%",
"62%"
],
"avoidLabelOverlap": true,
"label": {
"color": "#cfd3dc"
},
"labelLine": {
"lineStyle": {
"color": "#a3a6ad"
}
},
"encode": {
"itemName": "channel",
"value": "value"
}
}
]
}
},
"style": null,
"events": []
}
}
@@ -0,0 +1,213 @@
{
"order": 15,
"key": "progress-ring-chart",
"type": "progress-ring-chart",
"name": "进度环",
"description": "用环形进度条展示单个指标的完成百分比",
"tags": [
"charts",
"进度环",
"progress-ring-chart",
"进度",
"完成率",
"百分比"
],
"renderer": "echarts",
"group": "charts",
"icon": "solar:pie-chart-3-linear",
"sample_data": true,
"usage_notes": [
"默认图表数据仅用于演示,不是真实业务数据",
"通过顶层 dataId 关联已有数据源,当前后端尚未实现绑定操作",
"基于 gauge 系列,数据源取第一行,前端按 encode 转成 series.data"
],
"field_groups": [
"node-base",
"chart-title",
"chart-gauge-value",
{
"key": "progress-ring-shape",
"label": "环形",
"description": "进度环和底环的粗细与端点",
"fields": [
{
"path": "props.option.series.0.progress.width",
"label": "环宽度",
"description": "进度环的粗细",
"value_type": "number",
"span": 12,
"minimum": 1,
"maximum": 60,
"unit": "px",
"aliases": [
"进度环宽度",
"环的粗细"
]
},
{
"path": "props.option.series.0.axisLine.lineStyle.width",
"label": "底环宽度",
"description": "进度环下方灰色底环的粗细,通常与环宽度相同",
"value_type": "number",
"span": 12,
"minimum": 1,
"maximum": 60,
"unit": "px"
},
{
"path": "props.option.series.0.progress.roundCap",
"label": "圆角端点",
"description": "开启后进度环两端为圆角",
"value_type": "boolean",
"aliases": [
"圆角"
]
}
]
},
"chart-encode-item"
],
"eventOptions": [
{
"label": "点击事件",
"value": "click"
},
{
"label": "双击事件",
"value": "dblclick"
},
{
"label": "鼠标进入事件",
"value": "mouseenter"
},
{
"label": "鼠标离开事件",
"value": "mouseleave"
},
{
"label": "鼠标按下事件",
"value": "mousedown"
},
{
"label": "鼠标抬起事件",
"value": "mouseup"
},
{
"label": "鼠标移动事件",
"value": "mousemove"
},
{
"label": "鼠标滚轮事件",
"value": "mousewheel"
},
{
"label": "生命周期事件",
"value": "vnodeMounted"
}
],
"schema": {
"type": "progress-ring-chart",
"name": "进度环",
"layout": {
"x": 0,
"y": 0,
"width": 260,
"height": 260
},
"props": {
"option": {
"title": {
"text": "年度目标完成率",
"top": 8,
"left": "center",
"textStyle": {
"color": "#e5eaf3",
"fontSize": 16
}
},
"tooltip": {
"trigger": "item",
"formatter": "{b}: {c}%"
},
"dataset": {
"source": [
{
"name": "完成率",
"value": 68
}
]
},
"series": [
{
"type": "gauge",
"min": 0,
"max": 100,
"startAngle": 90,
"endAngle": -270,
"center": [
"50%",
"56%"
],
"radius": "72%",
"pointer": {
"show": false
},
"progress": {
"show": true,
"roundCap": true,
"width": 14,
"itemStyle": {
"color": "#409eff"
}
},
"axisLine": {
"lineStyle": {
"width": 14,
"color": [
[
1,
"rgba(255, 255, 255, 0.1)"
]
]
}
},
"axisTick": {
"show": false
},
"splitLine": {
"show": false
},
"axisLabel": {
"show": false
},
"title": {
"offsetCenter": [
0,
"28%"
],
"color": "#cfd3dc",
"fontSize": 13
},
"detail": {
"valueAnimation": true,
"offsetCenter": [
0,
"-8%"
],
"color": "#e5eaf3",
"fontSize": 30,
"fontWeight": "bold",
"formatter": "{value}%"
},
"encode": {
"itemName": "name",
"value": "value"
}
}
]
}
},
"style": null,
"events": []
}
}
+308
View File
@@ -0,0 +1,308 @@
{
"order": 7,
"key": "radar-chart",
"type": "radar-chart",
"name": "雷达图",
"description": "在多个维度上对比一个或多个对象的得分",
"tags": [
"charts",
"雷达图",
"radar-chart"
],
"renderer": "echarts",
"group": "charts",
"icon": "solar:radar-2-bold",
"sample_data": true,
"usage_notes": [
"默认图表数据仅用于演示,不是真实业务数据",
"通过顶层 dataId 关联已有数据源,当前后端尚未实现绑定操作"
],
"field_groups": [
"node-base",
"chart-title",
"chart-legend",
"chart-palette",
{
"key": "radar-series",
"label": "线条",
"description": "各对象雷达线的颜色和填充",
"fields": [
{
"path": "props.option.color.0",
"label": "第一条线颜色",
"description": "第一个对象(数据集第一行)的雷达线颜色",
"value_type": "color",
"aliases": [
"颜色",
"主色"
]
},
{
"path": "props.option.color.1",
"label": "第二条线颜色",
"description": "第二个对象(数据集第二行)的雷达线颜色",
"value_type": "color"
},
{
"path": "props.option.series.0.lineStyle.width",
"label": "线宽",
"description": "雷达线的粗细",
"value_type": "number",
"minimum": 1,
"maximum": 12,
"unit": "px",
"aliases": [
"线条粗细"
],
"panel": false
},
{
"path": "props.option.series.0.areaStyle.opacity",
"label": "填充透明度",
"description": "雷达线围成区域的填充透明度,0 为不填充,1 为不透明",
"value_type": "number",
"minimum": 0,
"maximum": 1,
"aliases": [
"填充",
"透明度"
],
"panel": false
}
]
},
{
"key": "radar-axis",
"label": "维度",
"description": "雷达图的各个维度及其文字样式",
"fields": [
{
"path": "props.option.radar.indicator",
"label": "维度",
"description": "雷达图的维度列表,每项为 {name: 维度名称, max: 满分},顺序与数值字段一一对应",
"value_type": "array",
"item_type": "object",
"aliases": [
"指标",
"维度名称",
"满分"
],
"panel": false
},
{
"path": "props.option.radar.axisName.color",
"label": "维度文字色",
"description": "维度名称文字的颜色",
"value_type": "color",
"aliases": [
"维度颜色",
"指标文字颜色"
],
"panel": false
}
]
},
{
"key": "radar-data",
"label": "数据映射",
"description": "雷达图从数据集(dataset.source)中取哪些列",
"fields": [
{
"path": "props.option.series.0.encode.itemName",
"label": "名称字段",
"description": "每个对象(每条雷达线)的名称使用数据集中的哪一列",
"value_type": "string",
"aliases": [
"对象字段",
"类目字段"
]
},
{
"path": "props.option.series.0.encode.value",
"label": "数值字段",
"description": "各维度的得分依次使用数据集中的哪些列,顺序与维度列表一一对应",
"value_type": "array",
"item_type": "string",
"aliases": [
"维度字段",
"得分字段"
],
"panel": false
}
]
}
],
"eventOptions": [
{
"label": "点击事件",
"value": "click"
},
{
"label": "双击事件",
"value": "dblclick"
},
{
"label": "鼠标进入事件",
"value": "mouseenter"
},
{
"label": "鼠标离开事件",
"value": "mouseleave"
},
{
"label": "鼠标按下事件",
"value": "mousedown"
},
{
"label": "鼠标抬起事件",
"value": "mouseup"
},
{
"label": "鼠标移动事件",
"value": "mousemove"
},
{
"label": "鼠标滚轮事件",
"value": "mousewheel"
},
{
"label": "生命周期事件",
"value": "vnodeMounted"
}
],
"schema": {
"type": "radar-chart",
"name": "雷达图",
"layout": {
"x": 0,
"y": 0,
"width": 400,
"height": 280
},
"props": {
"option": {
"color": [
"#409eff",
"#67c23a",
"#e6a23c",
"#f56c6c",
"#909399"
],
"legend": {
"top": 38,
"left": "center",
"itemWidth": 12,
"itemHeight": 8,
"show": true,
"textStyle": {
"color": "#cfd3dc"
}
},
"title": {
"text": "店铺服务评分",
"top": 8,
"left": "center",
"textStyle": {
"color": "#e5eaf3",
"fontSize": 16
}
},
"tooltip": {},
"radar": {
"center": [
"50%",
"62%"
],
"radius": "58%",
"indicator": [
{
"name": "商品质量",
"max": 100
},
{
"name": "物流速度",
"max": 100
},
{
"name": "服务态度",
"max": 100
},
{
"name": "描述相符",
"max": 100
},
{
"name": "售后保障",
"max": 100
}
],
"axisName": {
"color": "#cfd3dc"
},
"splitLine": {
"lineStyle": {
"color": "rgba(255, 255, 255, 0.12)"
}
},
"splitArea": {
"areaStyle": {
"color": [
"rgba(255, 255, 255, 0.03)",
"rgba(255, 255, 255, 0.06)"
]
}
},
"axisLine": {
"lineStyle": {
"color": "rgba(255, 255, 255, 0.16)"
}
}
},
"dataset": {
"source": [
{
"name": "店铺A",
"sales": 80,
"market": 70,
"develop": 90,
"support": 85,
"tech": 75
},
{
"name": "店铺B",
"sales": 65,
"market": 88,
"develop": 72,
"support": 78,
"tech": 92
}
]
},
"series": [
{
"name": "评分",
"type": "radar",
"encode": {
"itemName": "name",
"value": [
"sales",
"market",
"develop",
"support",
"tech"
]
},
"lineStyle": {
"width": 2
},
"areaStyle": {
"opacity": 0.18
}
}
]
}
},
"style": null,
"events": []
}
}
+178
View File
@@ -0,0 +1,178 @@
{
"order": 5,
"key": "ring-chart",
"type": "ring-chart",
"name": "环形图",
"description": "中间留空的饼图,展示各部分占整体的比例",
"tags": [
"charts",
"环形图",
"ring-chart"
],
"renderer": "echarts",
"group": "charts",
"icon": "solar:pie-chart-3-bold",
"sample_data": true,
"usage_notes": [
"默认图表数据仅用于演示,不是真实业务数据",
"通过顶层 dataId 关联已有数据源,当前后端尚未实现绑定操作"
],
"field_groups": [
"node-base",
"chart-title",
"chart-legend",
"chart-palette",
"chart-item-series",
{
"key": "ring-shape",
"label": "环形",
"description": "环形的内外半径",
"fields": [
{
"path": "props.option.series.0.radius",
"label": "内外半径",
"description": "两个百分比依次为内半径和外半径,相对组件较短边的一半,例如 [\"42%\", \"68%\"];内半径越大环越细",
"value_type": "array",
"item_type": "string",
"aliases": [
"环宽",
"半径",
"环的粗细"
],
"panel": false
}
]
},
"chart-encode-item"
],
"eventOptions": [
{
"label": "点击事件",
"value": "click"
},
{
"label": "双击事件",
"value": "dblclick"
},
{
"label": "鼠标进入事件",
"value": "mouseenter"
},
{
"label": "鼠标离开事件",
"value": "mouseleave"
},
{
"label": "鼠标按下事件",
"value": "mousedown"
},
{
"label": "鼠标抬起事件",
"value": "mouseup"
},
{
"label": "鼠标移动事件",
"value": "mousemove"
},
{
"label": "鼠标滚轮事件",
"value": "mousewheel"
},
{
"label": "生命周期事件",
"value": "vnodeMounted"
}
],
"schema": {
"type": "ring-chart",
"name": "环形图",
"layout": {
"x": 0,
"y": 0,
"width": 400,
"height": 260
},
"props": {
"option": {
"color": [
"#409eff",
"#67c23a",
"#e6a23c",
"#f56c6c",
"#909399"
],
"legend": {
"top": 38,
"left": "center",
"itemWidth": 12,
"itemHeight": 8,
"show": true,
"textStyle": {
"color": "#cfd3dc"
}
},
"title": {
"text": "设备占比",
"top": 8,
"left": "center",
"textStyle": {
"color": "#e5eaf3",
"fontSize": 16
}
},
"tooltip": {
"trigger": "item"
},
"dataset": {
"source": [
{
"device": "桌面端",
"value": 1048
},
{
"device": "移动端",
"value": 735
},
{
"device": "平板",
"value": 380
},
{
"device": "其他",
"value": 184
}
]
},
"series": [
{
"name": "设备",
"type": "pie",
"radius": [
"42%",
"68%"
],
"center": [
"50%",
"62%"
],
"avoidLabelOverlap": true,
"label": {
"color": "#cfd3dc"
},
"labelLine": {
"lineStyle": {
"color": "#a3a6ad"
}
},
"encode": {
"itemName": "device",
"value": "value"
}
}
]
}
},
"style": null,
"events": []
}
}
+211
View File
@@ -0,0 +1,211 @@
{
"order": 12,
"key": "rose-chart",
"type": "rose-chart",
"name": "玫瑰图",
"description": "用扇区半径展示各类目数值大小的饼图变体,差异比普通饼图更醒目",
"tags": [
"charts",
"玫瑰图",
"rose-chart",
"南丁格尔",
"占比"
],
"renderer": "echarts",
"group": "charts",
"icon": "solar:pie-chart-bold",
"sample_data": true,
"usage_notes": [
"默认图表数据仅用于演示,不是真实业务数据",
"通过顶层 dataId 关联已有数据源,当前后端尚未实现绑定操作"
],
"field_groups": [
"node-base",
"chart-title",
"chart-legend",
"chart-palette",
"chart-item-series",
{
"key": "rose-shape",
"label": "玫瑰",
"description": "玫瑰图的类型和半径",
"fields": [
{
"path": "props.option.series.0.roseType",
"label": "玫瑰类型",
"description": "扇区如何体现数值",
"value_type": "enum",
"options": [
{
"value": "area",
"label": "面积模式",
"description": "各扇区圆心角相同,半径体现数值"
},
{
"value": "radius",
"label": "半径模式",
"description": "圆心角和半径都体现数值"
}
],
"aliases": [
"模式"
]
},
{
"path": "props.option.series.0.radius",
"label": "内外半径",
"description": "两个百分比依次为内半径和外半径,相对组件较短边的一半,例如 [\"12%\", \"62%\"]",
"value_type": "array",
"item_type": "string",
"aliases": [
"半径"
],
"panel": false
}
]
},
"chart-encode-item"
],
"eventOptions": [
{
"label": "点击事件",
"value": "click"
},
{
"label": "双击事件",
"value": "dblclick"
},
{
"label": "鼠标进入事件",
"value": "mouseenter"
},
{
"label": "鼠标离开事件",
"value": "mouseleave"
},
{
"label": "鼠标按下事件",
"value": "mousedown"
},
{
"label": "鼠标抬起事件",
"value": "mouseup"
},
{
"label": "鼠标移动事件",
"value": "mousemove"
},
{
"label": "鼠标滚轮事件",
"value": "mousewheel"
},
{
"label": "生命周期事件",
"value": "vnodeMounted"
}
],
"schema": {
"type": "rose-chart",
"name": "玫瑰图",
"layout": {
"x": 0,
"y": 0,
"width": 400,
"height": 260
},
"props": {
"option": {
"color": [
"#409eff",
"#67c23a",
"#e6a23c",
"#f56c6c",
"#909399",
"#b37feb"
],
"legend": {
"top": 38,
"left": "center",
"itemWidth": 12,
"itemHeight": 8,
"show": true,
"textStyle": {
"color": "#cfd3dc"
}
},
"title": {
"text": "区域销售分布",
"top": 8,
"left": "center",
"textStyle": {
"color": "#e5eaf3",
"fontSize": 16
}
},
"tooltip": {
"trigger": "item"
},
"dataset": {
"source": [
{
"region": "华东",
"value": 40
},
{
"region": "华南",
"value": 33
},
{
"region": "华北",
"value": 28
},
{
"region": "西南",
"value": 22
},
{
"region": "华中",
"value": 20
},
{
"region": "西北",
"value": 15
}
]
},
"series": [
{
"name": "区域",
"type": "pie",
"roseType": "area",
"radius": [
"12%",
"62%"
],
"center": [
"50%",
"62%"
],
"itemStyle": {
"borderRadius": 4
},
"label": {
"color": "#cfd3dc"
},
"labelLine": {
"lineStyle": {
"color": "#a3a6ad"
}
},
"encode": {
"itemName": "region",
"value": "value"
}
}
]
}
},
"style": null,
"events": []
}
}
+232
View File
@@ -0,0 +1,232 @@
{
"order": 6,
"key": "scatter-chart",
"type": "scatter-chart",
"name": "散点图",
"description": "用点的横纵位置展示两个数值指标之间的关系和分布",
"tags": [
"charts",
"散点图",
"scatter-chart"
],
"renderer": "echarts",
"group": "charts",
"icon": "solar:chart-square-bold",
"sample_data": true,
"usage_notes": [
"默认图表数据仅用于演示,不是真实业务数据",
"通过顶层 dataId 关联已有数据源,当前后端尚未实现绑定操作"
],
"field_groups": [
"node-base",
"chart-title",
"chart-legend",
"chart-grid",
"chart-axis",
{
"key": "scatter-series",
"label": "散点",
"description": "散点的颜色、大小和系列名称",
"fields": [
{
"path": "props.option.series.0.itemStyle.color",
"label": "点颜色",
"description": "散点的颜色",
"value_type": "color",
"aliases": [
"颜色",
"散点颜色",
"主色"
]
},
{
"path": "props.option.series.0.symbolSize",
"label": "点大小",
"description": "散点的直径",
"value_type": "number",
"minimum": 1,
"maximum": 60,
"unit": "px",
"aliases": [
"散点大小",
"点的大小"
],
"panel": false
},
{
"path": "props.option.series.0.name",
"label": "系列名称",
"description": "这组散点的名称,显示在图例和鼠标悬停提示中;不是图表标题",
"value_type": "string",
"aliases": [
"图例名称",
"系列名",
"数据名称"
],
"panel": false
}
]
},
"chart-encode-xy"
],
"eventOptions": [
{
"label": "点击事件",
"value": "click"
},
{
"label": "双击事件",
"value": "dblclick"
},
{
"label": "鼠标进入事件",
"value": "mouseenter"
},
{
"label": "鼠标离开事件",
"value": "mouseleave"
},
{
"label": "鼠标按下事件",
"value": "mousedown"
},
{
"label": "鼠标抬起事件",
"value": "mouseup"
},
{
"label": "鼠标移动事件",
"value": "mousemove"
},
{
"label": "鼠标滚轮事件",
"value": "mousewheel"
},
{
"label": "生命周期事件",
"value": "vnodeMounted"
}
],
"schema": {
"type": "scatter-chart",
"name": "散点图",
"layout": {
"x": 0,
"y": 0,
"width": 400,
"height": 260
},
"props": {
"option": {
"legend": {
"top": 38,
"left": "center",
"itemWidth": 12,
"itemHeight": 8,
"show": true,
"textStyle": {
"color": "#cfd3dc"
}
},
"title": {
"text": "访客数与销售额",
"top": 8,
"left": "center",
"textStyle": {
"color": "#e5eaf3",
"fontSize": 16
}
},
"tooltip": {
"trigger": "item"
},
"dataset": {
"source": [
{
"users": 12,
"sales": 32
},
{
"users": 28,
"sales": 48
},
{
"users": 35,
"sales": 41
},
{
"users": 47,
"sales": 72
},
{
"users": 58,
"sales": 66
},
{
"users": 69,
"sales": 88
},
{
"users": 81,
"sales": 94
},
{
"users": 90,
"sales": 80
}
]
},
"grid": {
"top": 86,
"right": 24,
"bottom": 32,
"left": 48,
"containLabel": true
},
"xAxis": {
"type": "value",
"axisLine": {
"lineStyle": {
"color": "#4c4d4f"
}
},
"axisLabel": {
"color": "#cfd3dc"
},
"splitLine": {
"lineStyle": {
"color": "rgba(255, 255, 255, 0.08)"
}
}
},
"yAxis": {
"type": "value",
"axisLabel": {
"color": "#cfd3dc"
},
"splitLine": {
"lineStyle": {
"color": "rgba(255, 255, 255, 0.08)"
}
}
},
"series": [
{
"name": "店铺",
"type": "scatter",
"symbolSize": 12,
"encode": {
"x": "users",
"y": "sales"
},
"itemStyle": {
"color": "#409eff"
}
}
]
}
},
"style": null,
"events": []
}
}
@@ -0,0 +1,348 @@
{
"order": 10,
"key": "stacked-bar-chart",
"type": "stacked-bar-chart",
"name": "堆叠柱状图",
"description": "多个系列在同一柱子上堆叠,同时展示总量和各部分构成",
"tags": [
"charts",
"堆叠柱状图",
"stacked-bar-chart",
"堆叠",
"构成"
],
"renderer": "echarts",
"group": "charts",
"icon": "solar:chart-bold",
"sample_data": true,
"usage_notes": [
"默认图表数据仅用于演示,不是真实业务数据",
"通过顶层 dataId 关联已有数据源,当前后端尚未实现绑定操作",
"三个系列共用 stack: total,X 字段需在三个系列的 encode.x 中保持一致"
],
"field_groups": [
"node-base",
"chart-title",
"chart-legend",
"chart-grid",
"chart-axis",
"chart-palette",
{
"key": "stacked-series",
"label": "系列",
"description": "三个堆叠系列的颜色和名称,从下往上依次为系列1、2、3",
"fields": [
{
"path": "props.option.color.0",
"label": "系列1颜色",
"description": "第 1 个系列(从下往上第 1 段)的颜色",
"value_type": "color",
"span": 12,
"aliases": [
"颜色",
"主色"
]
},
{
"path": "props.option.color.1",
"label": "系列2颜色",
"description": "第 2 个系列(从下往上第 2 段)的颜色",
"value_type": "color",
"span": 12
},
{
"path": "props.option.color.2",
"label": "系列3颜色",
"description": "第 3 个系列(从下往上第 3 段)的颜色",
"value_type": "color",
"span": 12
},
{
"path": "props.option.series.0.name",
"label": "系列1名称",
"description": "第 1 个系列的名称,显示在图例和鼠标悬停提示中",
"value_type": "string",
"aliases": [
"图例名称"
],
"panel": false
},
{
"path": "props.option.series.1.name",
"label": "系列2名称",
"description": "第 2 个系列的名称,显示在图例和鼠标悬停提示中",
"value_type": "string",
"aliases": [
"图例名称"
],
"panel": false
},
{
"path": "props.option.series.2.name",
"label": "系列3名称",
"description": "第 3 个系列的名称,显示在图例和鼠标悬停提示中",
"value_type": "string",
"aliases": [
"图例名称"
],
"panel": false
}
]
},
{
"key": "stacked-data",
"label": "数据映射",
"description": "堆叠柱状图从数据集(dataset.source)中取哪些列",
"fields": [
{
"path": "props.option.series.0.encode.x",
"label": "X字段",
"description": "横轴类目使用数据集中的哪一列;三个系列必须相同,修改时同时修改系列2、系列3的X字段",
"value_type": "string",
"aliases": [
"横轴字段",
"类目字段"
]
},
{
"path": "props.option.series.1.encode.x",
"label": "系列2X字段",
"description": "须与X字段保持一致,修改X字段时同步修改",
"value_type": "string",
"panel": false
},
{
"path": "props.option.series.2.encode.x",
"label": "系列3X字段",
"description": "须与X字段保持一致,修改X字段时同步修改",
"value_type": "string",
"panel": false
},
{
"path": "props.option.series.0.encode.y",
"label": "系列1字段",
"description": "第 1 个系列的数值使用数据集中的哪一列",
"value_type": "string",
"aliases": [
"系列1数值字段"
]
},
{
"path": "props.option.series.1.encode.y",
"label": "系列2字段",
"description": "第 2 个系列的数值使用数据集中的哪一列",
"value_type": "string",
"aliases": [
"系列2数值字段"
]
},
{
"path": "props.option.series.2.encode.y",
"label": "系列3字段",
"description": "第 3 个系列的数值使用数据集中的哪一列",
"value_type": "string",
"aliases": [
"系列3数值字段"
]
}
]
}
],
"eventOptions": [
{
"label": "点击事件",
"value": "click"
},
{
"label": "双击事件",
"value": "dblclick"
},
{
"label": "鼠标进入事件",
"value": "mouseenter"
},
{
"label": "鼠标离开事件",
"value": "mouseleave"
},
{
"label": "鼠标按下事件",
"value": "mousedown"
},
{
"label": "鼠标抬起事件",
"value": "mouseup"
},
{
"label": "鼠标移动事件",
"value": "mousemove"
},
{
"label": "鼠标滚轮事件",
"value": "mousewheel"
},
{
"label": "生命周期事件",
"value": "vnodeMounted"
}
],
"schema": {
"type": "stacked-bar-chart",
"name": "堆叠柱状图",
"layout": {
"x": 0,
"y": 0,
"width": 400,
"height": 260
},
"props": {
"option": {
"color": [
"#409eff",
"#67c23a",
"#e6a23c",
"#f56c6c",
"#909399"
],
"legend": {
"top": 38,
"left": "center",
"itemWidth": 12,
"itemHeight": 8,
"show": true,
"textStyle": {
"color": "#cfd3dc"
}
},
"title": {
"text": "各渠道月度销售额",
"top": 8,
"left": "center",
"textStyle": {
"color": "#e5eaf3",
"fontSize": 16
}
},
"tooltip": {
"trigger": "axis",
"axisPointer": {
"type": "shadow"
}
},
"dataset": {
"source": [
{
"month": "一月",
"online": 120,
"offline": 220,
"agent": 150
},
{
"month": "二月",
"online": 132,
"offline": 182,
"agent": 212
},
{
"month": "三月",
"online": 101,
"offline": 191,
"agent": 201
},
{
"month": "四月",
"online": 134,
"offline": 234,
"agent": 154
},
{
"month": "五月",
"online": 90,
"offline": 290,
"agent": 190
},
{
"month": "六月",
"online": 230,
"offline": 330,
"agent": 330
}
]
},
"grid": {
"top": 86,
"right": 24,
"bottom": 32,
"left": 48,
"containLabel": true
},
"xAxis": {
"type": "category",
"axisLine": {
"lineStyle": {
"color": "#4c4d4f"
}
},
"axisLabel": {
"color": "#cfd3dc"
}
},
"yAxis": {
"type": "value",
"axisLabel": {
"color": "#cfd3dc"
},
"splitLine": {
"lineStyle": {
"color": "rgba(255, 255, 255, 0.08)"
}
}
},
"series": [
{
"name": "线上",
"type": "bar",
"stack": "total",
"barWidth": "45%",
"encode": {
"x": "month",
"y": "online"
},
"itemStyle": {}
},
{
"name": "门店",
"type": "bar",
"stack": "total",
"barWidth": "45%",
"encode": {
"x": "month",
"y": "offline"
},
"itemStyle": {}
},
{
"name": "代理",
"type": "bar",
"stack": "total",
"barWidth": "45%",
"encode": {
"x": "month",
"y": "agent"
},
"itemStyle": {
"borderRadius": [
4,
4,
0,
0
]
}
}
]
}
},
"style": null,
"events": []
}
}
+213
View File
@@ -0,0 +1,213 @@
{
"order": 16,
"key": "text",
"type": "text",
"name": "文本",
"description": "显示一段文字,常用作大屏标题、说明或标签",
"tags": [
"info",
"文本",
"text"
],
"renderer": "text",
"group": "info",
"icon": "solar:text-bold",
"sample_data": false,
"usage_notes": [
"events 中代码只作为资料保存,不由后端执行"
],
"field_groups": [
"node-base",
{
"key": "text-content",
"label": "内容",
"description": "文本组件显示的文字",
"fields": [
{
"path": "props.content",
"label": "内容",
"description": "文本组件显示的文字",
"value_type": "string",
"aliases": [
"文字",
"文本内容",
"标题文字",
"名字"
]
}
]
},
{
"key": "text-style",
"label": "文字样式",
"description": "文字的颜色、大小、粗细和对齐",
"fields": [
{
"path": "style.color",
"label": "颜色",
"description": "文字的颜色",
"value_type": "color",
"aliases": [
"文字颜色",
"字体颜色"
]
},
{
"path": "style.fontSize",
"label": "字号",
"description": "文字的大小",
"value_type": "number",
"minimum": 8,
"maximum": 200,
"unit": "px",
"aliases": [
"字体大小",
"文字大小"
]
},
{
"path": "style.textAlign",
"label": "水平对齐",
"description": "文字在组件内的水平位置",
"value_type": "enum",
"options": [
{
"value": "left",
"label": "左对齐"
},
{
"value": "center",
"label": "居中"
},
{
"value": "right",
"label": "右对齐"
}
],
"aliases": [
"对齐"
]
},
{
"path": "style.alignItems",
"label": "垂直对齐",
"description": "文字在组件内的垂直位置",
"value_type": "enum",
"options": [
{
"value": "flex-start",
"label": "顶部"
},
{
"value": "center",
"label": "居中"
},
{
"value": "flex-end",
"label": "底部"
}
]
},
{
"path": "style.fontWeight",
"label": "字重",
"description": "文字粗细",
"value_type": "enum",
"default": "normal",
"options": [
{
"value": "normal",
"label": "常规"
},
{
"value": "bold",
"label": "加粗"
}
],
"aliases": [
"加粗",
"粗细"
],
"panel": false
},
{
"path": "style.backgroundColor",
"label": "背景色",
"description": "文本组件的背景颜色,默认透明",
"value_type": "color",
"default": "transparent",
"aliases": [
"背景颜色",
"底色"
],
"panel": false
}
]
}
],
"eventOptions": [
{
"label": "点击事件",
"value": "click"
},
{
"label": "双击事件",
"value": "dblclick"
},
{
"label": "鼠标进入事件",
"value": "mouseenter"
},
{
"label": "鼠标离开事件",
"value": "mouseleave"
},
{
"label": "鼠标按下事件",
"value": "mousedown"
},
{
"label": "鼠标抬起事件",
"value": "mouseup"
},
{
"label": "鼠标移动事件",
"value": "mousemove"
},
{
"label": "鼠标滚轮事件",
"value": "mousewheel"
},
{
"label": "生命周期事件",
"value": "vnodeMounted"
}
],
"schema": {
"type": "text",
"name": "文本",
"layout": {
"x": 0,
"y": 0,
"width": 200,
"height": 50
},
"props": {
"content": "电商运营数据看板"
},
"style": {
"color": "#e5eaf3",
"fontSize": 16,
"textAlign": "center",
"alignItems": "center"
},
"events": [
{
"type": "click",
"title": "点击事件",
"name": "fn",
"code": "console.log(123)"
}
]
}
}
+3 -1
View File
@@ -9,7 +9,9 @@ class ActionValidationError(BaseModel):
action_index: int | None = Field(
default=None, ge=0, description="失败动作的下标,从 0 开始,整批错误为 null"
)
code: Literal["schema_error", "generation_error", "scope_error", "execution_error"]
code: Literal[
"schema_error", "generation_error", "scope_error", "field_error", "execution_error"
]
message: str
+19 -8
View File
@@ -1,8 +1,8 @@
"""精确更新组件 props 或 style 下的一个字段"""
"""精确更新组件名称,或 props / style 下的一个字段"""
from typing import Annotated, Literal
from typing import Annotated, Literal, Self
from pydantic import Field, JsonValue
from pydantic import Field, JsonValue, model_validator
from app.schemas.base import NonBlankString
@@ -13,10 +13,21 @@ PathSegment = NonBlankString | Annotated[int, Field(ge=0)]
class UpdateAction(BaseAction):
type: Literal["update"] = "update"
section: Literal["props", "style"] = Field(description="允许更新的组件属性区域")
section: Literal["name", "props", "style"] = Field(
description="字段路径的第一段:name 为组件名称,props / style 为组件属性区域"
)
path: list[PathSegment] = Field(
min_length=1, description="区域内的字段路径,字符串为对象键,整数为数组下标"
)
value: JsonValue = Field(
description="替换该路径的值,支持任意 JSON;null 是赋值,不表示删除"
description="字段路径第一段之后的各段,字符串为对象键,整数为数组下标;name 没有后续路径,传空列表"
)
value: JsonValue = Field(description="替换该字段的值,必须符合字段定义的类型和取值")
@model_validator(mode="after")
def path_matches_section(self) -> Self:
if (self.section == "name") != (not self.path):
raise ValueError("section 为 name 时 path 为空,props / style 至少一段路径")
return self
@property
def field_path(self) -> str:
"""与物料字段定义一致的点路径,例如 props.option.title.text"""
return ".".join([self.section, *map(str, self.path)])
+69 -17
View File
@@ -5,6 +5,7 @@ from typing import Literal, Self
from pydantic import BaseModel, ConfigDict, Field, model_validator
from app.schemas.base import ExtensibleModel, JsonObject, NonBlankString
from app.schemas.material_field import MISSING, FieldGroup, lookup
from app.schemas.screen import Layout, PositiveNumber
@@ -26,15 +27,6 @@ class MaterialTemplate(ExtensibleModel):
events: list[JsonObject] = Field(default_factory=list)
class MaterialSetter(ExtensibleModel):
type: NonBlankString = Field(description="前端属性控件类型,不是完整的值类型约束")
label: NonBlankString
key: NonBlankString = Field(
description="前端原始点路径,例如 props.option.series.0.itemStyle.color"
)
props: JsonObject = Field(default_factory=dict)
class ComponentDefinition(ExtensibleModel):
model_config = ConfigDict(serialize_by_alias=True)
key: NonBlankString = Field(description="目录中的唯一编号,不是 Screen 节点 ID")
@@ -52,22 +44,26 @@ class ComponentDefinition(ExtensibleModel):
schema_: MaterialTemplate = Field(
alias="schema", description="完整默认物料配置,不包含实例 ID"
)
setters: list[MaterialSetter] = Field(default_factory=list)
field_groups: list[NonBlankString | FieldGroup] = Field(
default_factory=list,
description="字段分组:字符串引用目录的共享分组,对象为本物料独有的分组;"
"前端属性面板的 setters 由它派生,不单独保存",
)
eventOptions: list[JsonObject] = Field(default_factory=list)
sample_data: bool = Field(
default=False, description="默认配置是否含示例数据,不能视为真实业务数据"
)
props_schema: JsonObject | None = Field(
default=None,
description="可选的 props JSON Schema,未提供表示未知,不表示无限制",
)
style_schema: JsonObject | None = Field(
default=None, description="可选的 style JSON Schema,仅保存定义,当前不执行校验"
)
usage_notes: list[NonBlankString] = Field(
default_factory=list, description="配置、数据绑定及使用限制说明"
)
def template(self) -> JsonObject:
"""默认配置的 JSON;style 为 null 表示未设置,字段默认值按路径不存在处理"""
data = self.schema_.model_dump(mode="json")
if data.get("style") is None:
data.pop("style", None)
return data
@model_validator(mode="after")
def consistent_type(self) -> Self:
if self.type != self.schema_.type:
@@ -77,17 +73,73 @@ class ComponentDefinition(ExtensibleModel):
return self
def _default_problem(field, template: JsonObject) -> str | None:
"""默认值只有一个来源:默认配置有该路径时取它,没有时字段必须声明 default"""
try:
current = lookup(template, field.segments)
except ValueError:
return "的路径与默认配置结构冲突"
if current is MISSING:
if field.default is None:
return "在默认配置中不存在,需要声明 default"
error = field.value_error(field.default)
return f"的 default {error}" if error else None
if field.default is not None:
return "在默认配置中已有值,不能再声明 default"
error = field.value_error(current)
return f"的默认配置值 {error}" if error else None
class ComponentCatalog(ExtensibleModel):
version: NonBlankString = Field(
description="前端组件目录版本,后续用于检查是否与前端匹配"
)
components: list[ComponentDefinition]
field_groups: list[FieldGroup] = Field(
default_factory=list, description="多个物料共用的字段分组,物料按 key 引用"
)
@model_validator(mode="after")
def unique_keys(self) -> Self:
keys = [component.key for component in self.components]
if len(keys) != len(set(keys)):
raise ValueError("组件目录 key 不能重复")
shared = [group.key for group in self.field_groups]
if len(shared) != len(set(shared)):
raise ValueError("共享字段分组 key 不能重复")
for component in self.components:
groups = [
group.key if isinstance(group, FieldGroup) else group
for group in component.field_groups
]
if len(groups) != len(set(groups)):
raise ValueError(f"物料 {component.key} 的字段分组 key 重复")
if missing := [
group
for group in component.field_groups
if isinstance(group, str) and group not in shared
]:
raise ValueError(f"物料 {component.key} 引用了不存在的共享分组: {missing}")
if shadowed := [
group.key
for group in component.field_groups
if isinstance(group, FieldGroup) and group.key in shared
]:
raise ValueError(f"物料 {component.key} 的独有分组与共享分组重名: {shadowed}")
by_key = {group.key: group for group in self.field_groups}
fields = [
field
for group in component.field_groups
for field in (by_key[group] if isinstance(group, str) else group).fields
]
paths = [field.path for field in fields]
if duplicated := sorted({path for path in paths if paths.count(path) > 1}):
raise ValueError(f"物料 {component.key} 的字段路径重复: {duplicated}")
template = component.template()
for field in fields:
problem = _default_problem(field, template)
if problem:
raise ValueError(f"物料 {component.key} 字段 {field.path} {problem}")
return self
+155
View File
@@ -0,0 +1,155 @@
"""物料字段定义:分组 → 字段,每个字段带路径、含义、值类型和取值约束
字段定义是“组件有哪些可改属性”的唯一来源:前端属性面板的 setters、
agent 查询字段、动作校验都由它派生,不再各自维护一份
"""
from typing import Literal, Self
from pydantic import Field, JsonValue, model_validator
from app.schemas.base import ExtensibleModel, NonBlankString
# 字段路径的第一段:节点名称、布局、组件属性、组件样式
FIELD_ROOTS = ("name", "layout", "props", "style")
ValueType = Literal[
"string", "number", "integer", "boolean", "color", "enum", "array", "object"
]
# 数组元素只用基础类型,枚举和嵌套数组没有需要的场景
ItemType = Literal["string", "number", "integer", "boolean", "color", "object"]
# 值类型对应的前端属性控件;array / object 没有通用控件,只能不在面板显示
PANEL_CONTROLS: dict[str, str] = {
"string": "input",
"number": "number",
"integer": "number",
"boolean": "checkbox",
"color": "color",
"enum": "select",
}
# 默认配置中没有该路径时的标记,区别于值为 null
MISSING = object()
# 值类型对应的 JSON 类型,用于检查默认配置里的现值
_JSON_TYPES: dict[str, type | tuple[type, ...]] = {
"string": str,
"color": str,
"number": (int, float),
"integer": int,
"boolean": bool,
"array": list,
"object": dict,
}
def _type_error(value_type: str, value: JsonValue) -> str | None:
# bool 是 int 的子类,数值类型需要单独排除
if not isinstance(value, _JSON_TYPES[value_type]) or (
value_type in ("number", "integer") and isinstance(value, bool)
):
return f"类型必须是 {value_type}"
return None
def lookup(value: JsonValue, segments: list[str | int]):
"""按路径取值;路径不存在返回 MISSING,路径中途遇到标量时抛 ValueError"""
for segment in segments:
if isinstance(value, dict) and isinstance(segment, str):
if segment not in value:
return MISSING
value = value[segment]
elif isinstance(value, list) and isinstance(segment, int):
if segment >= len(value):
return MISSING
value = value[segment]
else:
raise ValueError(f"路径无法继续访问: {segments}")
return value
class FieldOption(ExtensibleModel):
value: JsonValue
label: NonBlankString
description: NonBlankString | None = None
class MaterialField(ExtensibleModel):
path: NonBlankString = Field(
description="点路径,与前端 setter key 一致,数字段为数组下标,例如 props.option.series.0.itemStyle.color"
)
label: NonBlankString = Field(description="字段名称,属性面板显示的文字")
description: NonBlankString = Field(
description="字段含义、在画面上的效果,以及用户通常怎么称呼它"
)
value_type: ValueType
options: list[FieldOption] = Field(
default_factory=list, description="enum 的可选值,每个值可带说明"
)
item_type: ItemType | None = Field(
default=None, description="array 的元素类型"
)
minimum: int | float | None = None
maximum: int | float | None = None
unit: NonBlankString | None = Field(default=None, description="数值单位,例如 px")
default: JsonValue = Field(
default=None,
description="物料默认配置中没有该路径时的默认值;有该路径时以默认配置为准,不在这里重复",
)
aliases: list[NonBlankString] = Field(
default_factory=list, description="用户可能的叫法,供字段搜索使用"
)
editable: bool = Field(default=True, description="是否允许修改")
panel: bool = Field(default=True, description="是否显示在前端属性面板")
span: int | None = Field(default=None, description="属性面板栅格宽度,24 为整行")
@model_validator(mode="after")
def consistent(self) -> Self:
if self.path.split(".")[0] not in FIELD_ROOTS:
raise ValueError(f"字段路径必须以 {'/'.join(FIELD_ROOTS)} 开头: {self.path}")
if (self.value_type == "enum") != bool(self.options):
raise ValueError(f"只有 enum 字段提供 options,且 enum 必须提供: {self.path}")
if (self.value_type == "array") != (self.item_type is not None):
raise ValueError(f"只有 array 字段提供 item_type,且 array 必须提供: {self.path}")
if self.panel and self.value_type not in PANEL_CONTROLS:
raise ValueError(f"{self.value_type} 字段没有属性面板控件,panel 必须为 false: {self.path}")
return self
def value_error(self, value: JsonValue) -> str | None:
"""检查值是否符合字段类型和取值约束,符合返回 None"""
if self.value_type == "enum":
allowed = [option.value for option in self.options]
return None if value in allowed else f"取值必须是 {allowed} 之一"
if error := _type_error(self.value_type, value):
return error
if self.item_type is not None:
for element in value:
if error := _type_error(self.item_type, element):
return f"数组元素{error}"
if self.value_type in ("number", "integer"):
if self.minimum is not None and value < self.minimum:
return f"不能小于 {self.minimum}"
if self.maximum is not None and value > self.maximum:
return f"不能大于 {self.maximum}"
return None
@property
def segments(self) -> list[str | int]:
# 数字段视为数组下标,与前端 setter key 的解析方式一致
return [int(part) if part.isdigit() else part for part in self.path.split(".")]
class FieldGroup(ExtensibleModel):
key: NonBlankString = Field(description="分组编号,共享分组在整个目录内唯一")
label: NonBlankString
description: NonBlankString = Field(description="这一组字段管什么,供模型先看目录再查字段")
fields: list[MaterialField]
@model_validator(mode="after")
def unique_paths(self) -> Self:
paths = [field.path for field in self.fields]
if len(paths) != len(set(paths)):
raise ValueError(f"分组 {self.key} 中字段路径重复")
return self
+1 -1
View File
@@ -30,7 +30,7 @@ class RequirementChange(ExtensibleModel):
operation: NonBlankString = Field(description=f"需求操作。{OPERATION_GUIDE}")
parameters: JsonObject = Field(
default_factory=dict,
description="需求参数,例如 delta_x=50;保留用户要求的单位与相对变化语义",
description="需求参数,例如 delta_x=50;保留用户要求的单位与相对变化语义;属性修改用用户的说法描述改什么、改成什么,例如 {\"名字\": \"月度销售\"},不推断字段路径",
)
@field_validator("operation")
+29 -18
View File
@@ -1,4 +1,5 @@
"""物料目录的 MongoDB 存储:每个组件一条记录,目录版本放在元数据记录中"""
"""物料目录的 MongoDB 存储:每个组件一条记录,目录版本放在元数据记录中,
多个物料共用的字段分组单独存放,每个分组一条记录"""
from loguru import logger
from pymongo.asynchronous.database import AsyncDatabase
@@ -14,6 +15,7 @@ class MongoMaterialRepository:
def __init__(self, db: AsyncDatabase, prefix: str = ""):
# 集合名前缀用于测试隔离
self._materials = db.get_collection(f"{prefix}materials")
self._field_groups = db.get_collection(f"{prefix}material_field_groups")
async def load_all(self) -> ComponentCatalog | None:
# 没有元数据记录视为尚未导入,由调用方决定回退方式
@@ -21,33 +23,42 @@ class MongoMaterialRepository:
if meta is None:
return None
documents = await self._materials.find({"_id": {"$ne": META_ID}}).to_list()
components = [
{key: value for key, value in item.items() if key not in ("_id", "order")}
for item in sorted(documents, key=lambda item: item.get("order", 0))
]
groups = await self._field_groups.find({}).to_list()
return ComponentCatalog.model_validate(
{
"version": meta["version"],
"components": components,
"components": _ordered(documents),
"field_groups": _ordered(groups),
}
)
async def replace_all(self, catalog: ComponentCatalog) -> None:
# 整体替换目录,供导入脚本使用;order 保持导出文件里的组件顺序
documents = [
{
**component.model_dump(mode="json", by_alias=True),
"_id": component.key,
"order": index,
}
for index, component in enumerate(catalog.components)
]
await self._materials.delete_many({})
if documents:
await self._materials.insert_many(documents)
# 整体替换目录,供导入脚本使用;order 保持源文件中的顺序
for collection, items in (
(self._materials, catalog.components),
(self._field_groups, catalog.field_groups),
):
await collection.delete_many({})
if items:
await collection.insert_many(_documents(items))
await self._materials.insert_one({"_id": META_ID, "version": catalog.version})
def _documents(items) -> list[dict]:
# 记录 _id 为物料或分组的 key
return [
{**item.model_dump(mode="json", by_alias=True), "_id": item.key, "order": index}
for index, item in enumerate(items)
]
def _ordered(documents: list[dict]) -> list[dict]:
return [
{key: value for key, value in item.items() if key not in ("_id", "order")}
for item in sorted(documents, key=lambda item: item.get("order", 0))
]
async def warm_material_cache(db: AsyncDatabase | None) -> ComponentCatalog | None:
"""启动时把物料目录从 MongoDB 载入进程缓存;未启用或尚未导入时目录为空,不回退读文件"""
catalog = await MongoMaterialRepository(db).load_all() if db is not None else None
View File
+106
View File
@@ -0,0 +1,106 @@
"""物料字段 MCP 服务:只给 agent 的模型查询组件有哪些字段、字段含义和可取值
只监听本机,由应用启动时拉起,不对外暴露;查询逻辑在 app.resources.material_fields,
动作校验在主进程直接调用同一套逻辑,这里只是给模型使用的入口
单独运行:uv run python -X utf8 -m mcp_servers.material_server
"""
import asyncio
import os
from typing import Annotated, Any
from urllib.parse import urlsplit
from loguru import logger
from mcp.server.fastmcp import FastMCP
from pydantic import Field
from pymongo import AsyncMongoClient
from app.config import settings
from app.resources import material_fields
from app.schemas.component_resource import ComponentCatalog
from app.services.material_mongo import MongoMaterialRepository
_target = urlsplit(settings.mcp_material_url)
mcp = FastMCP(
"materials",
host="127.0.0.1",
# 由应用拉起时通过 PORT 传入,单独运行时取配置地址中的端口
port=int(os.environ.get("PORT") or _target.port or 8201),
streamable_http_path=_target.path or "/mcp",
# 工具只读且无会话状态,每次调用独立处理
stateless_http=True,
)
# 启动时从 MongoDB 载入,与主进程读取同一份目录;返回结果带版本,主进程据此核对
_catalog: ComponentCatalog | None = None
ComponentType = Annotated[
str, Field(description="物料类型,即大屏节点的 type,例如 bar-chart")
]
def _require_catalog() -> ComponentCatalog:
if _catalog is None:
raise RuntimeError("物料目录未载入")
return _catalog
@mcp.tool()
async def list_field_groups(component_type: ComponentType) -> dict[str, Any]:
"""列出物料的字段分组:每组管什么、包含哪些字段名称
先看分组目录,再用 get_fields 查看相关分组的完整字段定义
"""
return material_fields.list_field_groups(_require_catalog(), component_type)
@mcp.tool()
async def get_fields(
component_type: ComponentType,
group_keys: Annotated[
list[str], Field(description="要查看的分组 key,可一次传多个", min_length=1)
],
) -> dict[str, Any]:
"""查看分组内的完整字段定义:路径、含义、值类型、可选值、取值范围和默认值
修改组件属性只能使用这里返回的字段路径,值须符合字段的类型和取值约束
"""
return material_fields.get_fields(_require_catalog(), component_type, group_keys)
@mcp.tool()
async def search_fields(
component_type: ComponentType,
query: Annotated[
str, Field(description="用户对属性的说法,例如“名字”“柱子颜色”“数值标签”")
],
) -> dict[str, Any]:
"""按名称、别名、路径和说明搜索字段,按相关度返回完整字段定义
用户的说法对不上分组时使用;多个结果时根据字段说明判断用户指的是哪一个
"""
return material_fields.search_fields(_require_catalog(), component_type, query)
async def _load_catalog() -> ComponentCatalog:
if not settings.mongodb_uri:
raise SystemExit("未配置 MONGODB_URI,物料字段服务无法载入目录")
client = AsyncMongoClient(settings.mongodb_uri, serverSelectionTimeoutMS=5000)
try:
catalog = await MongoMaterialRepository(client[settings.mongodb_db]).load_all()
finally:
await client.close()
if catalog is None:
raise SystemExit("materials 集合尚未导入,物料字段服务无法载入目录")
return catalog
if __name__ == "__main__":
_catalog = asyncio.run(_load_catalog())
logger.info(
"物料字段服务已载入 {} 个物料,版本 {}",
len(_catalog.components),
_catalog.version,
)
mcp.run(transport="streamable-http")
+2
View File
@@ -16,4 +16,6 @@ dependencies = [
"pymongo>=4.17.0",
"langgraph-checkpoint-mongodb>=0.5.0",
"httpx>=0.28.1",
"mcp>=1.28.1",
"langchain-mcp-adapters>=0.3.0",
]
Generated
+450
View File
@@ -51,6 +51,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/12/b8/4bd346e22b28902df4d651910f5242c28d84e4a5c2435ca5c3f797ed7e2e/anyio-4.15.1-py3-none-any.whl", hash = "sha256:6152fdbbf9a77fdec97731721bebf7c4c44f7c29b424b0065826173efc7ed101", size = 132079, upload-time = "2026-09-05T10:42:37.923Z" },
]
[[package]]
name = "attrs"
version = "26.1.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" },
]
[[package]]
name = "backoff"
version = "2.2.1"
@@ -69,6 +78,104 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" },
]
[[package]]
name = "cffi"
version = "2.1.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz", hash = "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be", size = 530807, upload-time = "2026-08-03T21:21:18.939Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/70/d2/16d99a0c4948febc0ebd133a13b2f688ff7f8cb04da971e1128872ce0c03/cffi-2.1.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:c8d2c9fd1f2d16f780d15127abb050d13d1a76c03a4bd87d7e4980e45e511e12", size = 183838, upload-time = "2026-08-03T21:19:29.637Z" },
{ url = "https://files.pythonhosted.org/packages/cd/95/31b535a9f0220ae9f357de4a08d57ce89cb417653c2fd9f075f50822a388/cffi-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:398aff33cee2767e3e781d2554c54bd0dff386bb437581e0d8011fde1a942ec1", size = 184168, upload-time = "2026-08-03T21:19:30.764Z" },
{ url = "https://files.pythonhosted.org/packages/ad/5a/4707a0dc1f203f5dde5a907b0d4e3c25d71120241048bd5bc6f1bb9d4e71/cffi-2.1.1-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:154852545011f779917b11c78db2358d095da62a9a172b78ad0a583ee5adc0d0", size = 211805, upload-time = "2026-08-03T21:19:31.867Z" },
{ url = "https://files.pythonhosted.org/packages/ad/66/c19feabb28485b6e0bbaaafa90837a1ef5d302e90f2178bd33f17a49879b/cffi-2.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3311ed60d36f83378794e1009ac6258bafbf81f7888b4caa7b35a521e3f95813", size = 218716, upload-time = "2026-08-03T21:19:32.896Z" },
{ url = "https://files.pythonhosted.org/packages/a7/92/500760486c8baab49a7a8a58ba7fc3355ec3974b454b8a09e528efde9e1d/cffi-2.1.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6e192623c49c94421616a5778fba35cf0d5a8d000650c1967ef4448ee5cdd990", size = 205569, upload-time = "2026-08-03T21:19:34.142Z" },
{ url = "https://files.pythonhosted.org/packages/a5/a7/a67c733254d6e7373f7822f8082d8d6beade791e0cf12a7611f376fa61c7/cffi-2.1.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a6e721d4b0e45d5b65e87534470e67b18dcd092c83f68fba09f152b9cbc061af", size = 204907, upload-time = "2026-08-03T21:19:35.174Z" },
{ url = "https://files.pythonhosted.org/packages/f7/a4/4399daaf8f7dfee9d7c3327fdb0426ee041cc63edc358b93911ceb2bfc7a/cffi-2.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:34e261f78cb6ceaaa36f42f2613f4380d94d9c759a9c73c769ee6e0247364632", size = 217807, upload-time = "2026-08-03T21:19:36.286Z" },
{ url = "https://files.pythonhosted.org/packages/28/f7/dabe6da2466ecbd82dc62e7342dc6b1065dad990c06f00f0ede9ebf2a0ed/cffi-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7225e4514edb64eb6740324353e0da0711954fd8d7da4576755b1c6e09b697cd", size = 221252, upload-time = "2026-08-03T21:19:37.416Z" },
{ url = "https://files.pythonhosted.org/packages/ce/87/616202d8e51342c07d2534c510111c4cc37201775ce8f60802c9335d1edd/cffi-2.1.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df913725b79db7bcf03448f36b7bf8815363417d5b58deecf9305e3e30f0f21a", size = 214214, upload-time = "2026-08-03T21:19:38.507Z" },
{ url = "https://files.pythonhosted.org/packages/b4/c6/ab025d75d2c26c19b087c0124e75ee31cb65032f4fe345d356d8c507ab97/cffi-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f5cfbc5fe74540d335175b656c725d74d90e3730c626d92575eea35029d9afaa", size = 219408, upload-time = "2026-08-03T21:19:39.809Z" },
{ url = "https://files.pythonhosted.org/packages/db/e2/7e8109f65445bdc673a7b54f02c677de462db75674220fd1335efc8eb598/cffi-2.1.1-cp311-cp311-win32.whl", hash = "sha256:f8ec5e643a9a937f64e1999eb9f75d072263751912dc5cd06d3c85f8f44be7c3", size = 174470, upload-time = "2026-08-03T21:19:41.246Z" },
{ url = "https://files.pythonhosted.org/packages/73/c0/77ba02423c2f7d7091143c45cd49e0e6575c4c1967394bb542bd923a9b74/cffi-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:42f6930c31dc7f50732c9ae793c2786c7b6b044195967bbdde40bb9be81c4cc0", size = 185096, upload-time = "2026-08-03T21:19:42.615Z" },
{ url = "https://files.pythonhosted.org/packages/7c/47/9f1f85f9672ceda4984dc6c4f8824e8558992a2972c3d3c81fb8eb28d4ba/cffi-2.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:c7659f22557c5a0bc4855cd635f55edec690cc008a40768527762cb9fb263455", size = 179941, upload-time = "2026-08-03T21:19:43.747Z" },
{ url = "https://files.pythonhosted.org/packages/10/69/43965eccfdead3b9220015fd1320e117be8c6ed01a62ffab76eeb752f5d5/cffi-2.1.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:c8c69575568085ba0b1b10c0249d779a214aea6f6522e949a0fc9fb0fcb449d0", size = 184821, upload-time = "2026-08-03T21:19:44.887Z" },
{ url = "https://files.pythonhosted.org/packages/54/7d/16e5a096677b5e313ca80cd5e5170efa3ea44624a82bb111925522da64b1/cffi-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f81b3b8f3d4e343550fa4baa0e479bba9f2d29ce9c2e9b51d1ce1718d7442fcf", size = 184719, upload-time = "2026-08-03T21:19:46.129Z" },
{ url = "https://files.pythonhosted.org/packages/56/e6/8941622732edec876dd17d0453dce07317ae96db34f2ec1436c9d3785986/cffi-2.1.1-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:811bd1e21d32de12efca32393a0ab3f5133b54fce9bd44b8bd77ab07da14bf6a", size = 214799, upload-time = "2026-08-03T21:19:47.218Z" },
{ url = "https://files.pythonhosted.org/packages/44/de/f98430906df1545ffde0d543dd124a7a439bc2cd32b36b9c53f805df7333/cffi-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:68e62fe11f30d5ca8289242866f0a5291402d8529ca2178ab8afc5c9694ae890", size = 222389, upload-time = "2026-08-03T21:19:48.331Z" },
{ url = "https://files.pythonhosted.org/packages/6a/5b/717f1526b9957b34456313c31645c5b82b8fb5c3fe9e4752999be7128bfc/cffi-2.1.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4a7c934f7360e8cd64fe9efadcbd10c7c6364f531e432b9a4bf5ccbc9e0e8b50", size = 210249, upload-time = "2026-08-03T21:19:49.543Z" },
{ url = "https://files.pythonhosted.org/packages/64/b3/f8aa4f3e34986c7e4ec45072d1b1b9dd295b6b18007b45518d79726dd725/cffi-2.1.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:3143d81e29e1e20a9ce10901ec369012947876596f75a222235965f2b7ae832e", size = 208775, upload-time = "2026-08-03T21:19:50.918Z" },
{ url = "https://files.pythonhosted.org/packages/b1/db/dceb9dd5b231e1da801793f8acc9f3c52a7e1afe40bb1aae37e02b0faad5/cffi-2.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf", size = 221822, upload-time = "2026-08-03T21:19:52.054Z" },
{ url = "https://files.pythonhosted.org/packages/a0/d2/6cd24ae3be000a634109c247d1475d62e5616d0dc78c82770942ec384248/cffi-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:208f941bb9d18e768138677f0a6d2ce01f590df56043dda1df1535ac57c88517", size = 225232, upload-time = "2026-08-03T21:19:53.109Z" },
{ url = "https://files.pythonhosted.org/packages/cb/52/3fa190537004dd7f0ab860a6dc7c0175b8667f68d1e618a46f5498d30250/cffi-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:210019b6c7cf07f081b4c54635c8cf744377001350e29cc0f81c4377b4797735", size = 223597, upload-time = "2026-08-03T21:19:54.515Z" },
{ url = "https://files.pythonhosted.org/packages/80/fb/0bb75b7039588c074b37ae99f40d9bfddf990ecb2fbc346ebccd2e56b9be/cffi-2.1.1-cp312-cp312-win32.whl", hash = "sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e", size = 175292, upload-time = "2026-08-03T21:19:55.566Z" },
{ url = "https://files.pythonhosted.org/packages/d9/79/615cc094e2fb508cade7de88d3b4f6c4ec2bab695c97bce9153dc65aadf5/cffi-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:f53e442b08449d42821fa4a4fba000095af9f62742a500f978a9f557ec44339a", size = 185919, upload-time = "2026-08-03T21:19:56.89Z" },
{ url = "https://files.pythonhosted.org/packages/70/c6/d0ea84713fe46b243a436a18fcd47d639732747e21635c8a27191b06dc30/cffi-2.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:7bde5e4cc5c10140859842b9d383af292b22639a4dffb725314baf45968cef80", size = 180093, upload-time = "2026-08-03T21:19:58.155Z" },
{ url = "https://files.pythonhosted.org/packages/9d/f4/035513d4117049066b4779dc3b7c0c0fdad175fa13731c9f4003f1cd1478/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e", size = 194248, upload-time = "2026-08-03T21:19:59.399Z" },
{ url = "https://files.pythonhosted.org/packages/76/af/2aeb4dbb5fc41a04161ae9ff1518de7cec08e164f44a8ce6a4cf7fd2cd1d/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c", size = 196908, upload-time = "2026-08-03T21:20:00.746Z" },
{ url = "https://files.pythonhosted.org/packages/a7/46/2e5fdde8555706dd98139a910ca11be02809f3f605ce956f655d0214e100/cffi-2.1.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6", size = 184805, upload-time = "2026-08-03T21:20:02.02Z" },
{ url = "https://files.pythonhosted.org/packages/55/41/4c7042f317b9217502988f0873af87e16ad606dc20f84e546e3e6ce9764c/cffi-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971", size = 184764, upload-time = "2026-08-03T21:20:03.141Z" },
{ url = "https://files.pythonhosted.org/packages/43/1f/1c3d90d91811c8f86ced9ed637956c54bfe5b79ca98fe976d7f8c8979f6b/cffi-2.1.1-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c", size = 214722, upload-time = "2026-08-03T21:20:04.377Z" },
{ url = "https://files.pythonhosted.org/packages/37/6f/3b5ce4c3b2192d250f04908f2bfd91ef34552ec8f7716a5d4abdb8d67bb2/cffi-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125", size = 222369, upload-time = "2026-08-03T21:20:05.544Z" },
{ url = "https://files.pythonhosted.org/packages/02/10/4b3c75dde3d9663c9e02ba05c2668b954f671d4bbe346413ca8c696b295a/cffi-2.1.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264", size = 210175, upload-time = "2026-08-03T21:20:06.75Z" },
{ url = "https://files.pythonhosted.org/packages/df/62/14f74b9543e605d17701dc797b815958b8bb70b7624ce1b832ddad48ed6c/cffi-2.1.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3", size = 208670, upload-time = "2026-08-03T21:20:08.04Z" },
{ url = "https://files.pythonhosted.org/packages/95/95/86342356ff5953b3fb06f7ef7c5bee212d45e770abc7218d451b9148313c/cffi-2.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2", size = 221824, upload-time = "2026-08-03T21:20:09.274Z" },
{ url = "https://files.pythonhosted.org/packages/eb/ff/7b3429ff53aafe931ed8a5fc69f481bbef7ba6de87ddcbb63d08f483f613/cffi-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b", size = 225148, upload-time = "2026-08-03T21:20:10.7Z" },
{ url = "https://files.pythonhosted.org/packages/34/34/a95870b9221e09cf4f2ce3178b1a210abdfe63a1bd357da940418d7b8d15/cffi-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7", size = 223564, upload-time = "2026-08-03T21:20:12.165Z" },
{ url = "https://files.pythonhosted.org/packages/70/ea/839b50531021a647fb5e929f72cf97bc1ff702b5472166164b5b6e76b851/cffi-2.1.1-cp313-cp313-win32.whl", hash = "sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac", size = 175263, upload-time = "2026-08-03T21:20:13.559Z" },
{ url = "https://files.pythonhosted.org/packages/60/a6/8b149b2c3f2e11aaa1618ef64500b45f50f22c57a977a4dff1aff1f91042/cffi-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d", size = 185688, upload-time = "2026-08-03T21:20:14.69Z" },
{ url = "https://files.pythonhosted.org/packages/01/9a/11f687cb39d6a3504060d5242f04f48c735afb4d3d533958a20594890cb2/cffi-2.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973", size = 180078, upload-time = "2026-08-03T21:20:15.917Z" },
{ url = "https://files.pythonhosted.org/packages/d3/7b/d6bbf82b8b96e7391438898c42f5bd96dd02030fd5b64937d248220003e2/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7dbb61fe3a7699468030f71bbe5f8a0e326a151daa91beb11a6fc1f980c55e1c", size = 194064, upload-time = "2026-08-03T21:20:17.148Z" },
{ url = "https://files.pythonhosted.org/packages/94/e6/bcc91b283be94735e268487a054004f0aa19947b6348fa367db53230abc8/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:f24fb43132a4c6b4cb4eb029492919b2db645be6808d738f244fd146c03c32cb", size = 196720, upload-time = "2026-08-03T21:20:18.268Z" },
{ url = "https://files.pythonhosted.org/packages/d9/99/c4b0c17cacdc9c3b8f280026286a9826d6a208c0f047591a3c3ce99b91fd/cffi-2.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d28630f5854ab07ab1fd4aba756de52326c82e6be15d414b12793f1975048b54", size = 184964, upload-time = "2026-08-03T21:20:19.708Z" },
{ url = "https://files.pythonhosted.org/packages/b3/a9/9db617d05d7367c1ad0ab00b3aa6e6f9281edd689b4ee9ea0e5a84e89c97/cffi-2.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72", size = 184962, upload-time = "2026-08-03T21:20:20.833Z" },
{ url = "https://files.pythonhosted.org/packages/67/b8/b42132ca113dc567d37684437b46ca1dafc885902b02a110a02d5b511857/cffi-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1", size = 222328, upload-time = "2026-08-03T21:20:22.118Z" },
{ url = "https://files.pythonhosted.org/packages/80/10/c5c0cbf0a657aecf59ef511409734230bf556f05a0d6c9eed7aa5c0a0166/cffi-2.1.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:456a61fa52d579ebf9df2e9552ead5129855dbaff6c1e5a9b1bc408809bdc062", size = 209985, upload-time = "2026-08-03T21:20:23.401Z" },
{ url = "https://files.pythonhosted.org/packages/d5/6c/bfa0b87b03b9238148beca990292843c9396ba069b54496596594173de7b/cffi-2.1.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a4f00aa42f75d6e4595e8866e748cc1705adc0cddfeb2ca86d0d03993d63ba03", size = 208530, upload-time = "2026-08-03T21:20:24.628Z" },
{ url = "https://files.pythonhosted.org/packages/e9/02/4e7d553a7ac4b4238b38b3c1b80d486e9d4436f8d2acbf87a0997fe3f402/cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96", size = 221525, upload-time = "2026-08-03T21:20:25.758Z" },
{ url = "https://files.pythonhosted.org/packages/82/1d/a4aaf9babd75acb4d5f223bff71533bee748dd770a382619a798960ee9ba/cffi-2.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:64faea20f4e2613363a1a9b9c7dd73058f3ecd00133a511e72ad7c511658f527", size = 225053, upload-time = "2026-08-03T21:20:26.985Z" },
{ url = "https://files.pythonhosted.org/packages/81/10/5dc0e7bdd18e22107054288283380fc97a06ae3f1656a106908d666a3c88/cffi-2.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5c58fe613dc5e5336357eff555824a314d8e43282600435c8d1cb6a7a2fedd13", size = 223213, upload-time = "2026-08-03T21:20:28.277Z" },
{ url = "https://files.pythonhosted.org/packages/0b/e9/d0061c364cde06ee43168a0d076ac1da512cbc380d44767b844ba34fe2b6/cffi-2.1.1-cp314-cp314-win32.whl", hash = "sha256:1a18a57b58cfb21fc28d72e876acf10eaed67a1ed96226f92af4df681d571c4c", size = 177682, upload-time = "2026-08-03T21:20:44.288Z" },
{ url = "https://files.pythonhosted.org/packages/a7/06/1c3e01e3ba14c39f6d10bfbac52753b7e22259e38088e5cfe1d704918690/cffi-2.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:3222ba5d678f80a030e6afbcc33dc1ae5cb45facabb61cee2c7016b8432fde48", size = 187949, upload-time = "2026-08-03T21:20:45.623Z" },
{ url = "https://files.pythonhosted.org/packages/87/5b/da4e39efe18eeb89cf580ea9cfc66b6a7c3eadb808fc0cc1d3a295cb5a5d/cffi-2.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:ab36d55f9ed2d067327667c2fea18dda018eb628dd6347aa01dda6cf1f5d3836", size = 182947, upload-time = "2026-08-03T21:20:46.955Z" },
{ url = "https://files.pythonhosted.org/packages/23/59/40338bf421c5accea1d45158170c87006ef1cd371b05c077e76476949728/cffi-2.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7750c6449dff7864bb9bb27ddfb0267756189201a3afc911d82b3caacd70dfc3", size = 188504, upload-time = "2026-08-03T21:20:29.495Z" },
{ url = "https://files.pythonhosted.org/packages/7d/47/5ecf1023850036e674c77ec4de86182d309ae344e39e7cba984b7df5d647/cffi-2.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0beceaabe56af686895136a2de78db54ecd8e4046b236b8fd6d6cb61389e9bf2", size = 188259, upload-time = "2026-08-03T21:20:31.291Z" },
{ url = "https://files.pythonhosted.org/packages/2a/9c/92934c3bea9f785b23eba304538c0b4d37a2a96d2431eb3a1bc87a11aa19/cffi-2.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:49cbc70e6542d4ccccb936558d1064a8012541e78f821f955cff24e357776c94", size = 223864, upload-time = "2026-08-03T21:20:32.571Z" },
{ url = "https://files.pythonhosted.org/packages/4d/45/ba4c93527bc38616a8bd36488acb69a2212d60486794f0c1f318949bbb76/cffi-2.1.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e2d65b31f36619cda3999b78b2aa9632e76b78448e7a56fc4240824200e7c4fc", size = 211538, upload-time = "2026-08-03T21:20:33.808Z" },
{ url = "https://files.pythonhosted.org/packages/80/e9/b6ef565e452acb932fb0cb5443f44a78efbd1233e566f02b5a83855e9115/cffi-2.1.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:28907ab9bfb6aa13184cfc17c6b8e1023c5ab6fd7076d8c20a35e59fe04f8f29", size = 210688, upload-time = "2026-08-03T21:20:34.974Z" },
{ url = "https://files.pythonhosted.org/packages/9a/95/eff5f0cee78d2eabc7eebffec40d3fc1876b5f3c95582e018bb4b99601f2/cffi-2.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51b31d1c98274844cfd7838ce00bfc27c7423a4dc00fc0772fc3331c2cc90676", size = 223803, upload-time = "2026-08-03T21:20:36.564Z" },
{ url = "https://files.pythonhosted.org/packages/fa/01/579d39fb8bef00a335a23d83757b44feb24cd6345a2c451b64cb67b9c362/cffi-2.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e7cecbaadb83884793e05828cee59b210b24583b9c7425d0ba6a754fe22eb4e", size = 226763, upload-time = "2026-08-03T21:20:37.816Z" },
{ url = "https://files.pythonhosted.org/packages/8d/b0/0b44f47c60b01b57b6e2bbd92343f13a85a1d93bc46ccf6e47e244acd99c/cffi-2.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:25792eac27877609e7bb06d42ff88278a6624fff2ba9bbb523c09616b117e80f", size = 225688, upload-time = "2026-08-03T21:20:38.959Z" },
{ url = "https://files.pythonhosted.org/packages/eb/d2/3b7176cb570a1d3e27faf67b72f591af508036e0d8b2be2ef9af9e8c84bb/cffi-2.1.1-cp314-cp314t-win32.whl", hash = "sha256:8ef53b2de9bcb9197d31854256575d59dbac0cba72ac627bb291ef5eceb74be4", size = 182868, upload-time = "2026-08-03T21:20:40.388Z" },
{ url = "https://files.pythonhosted.org/packages/56/78/31f00c1bcd97c9bbf55f1bfdf5bc809a5de8887473e90bb9960dca825e80/cffi-2.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:616f097f2fe415bc92a247f02e11f634e1f9e9a83d327e3c915c15089c87869e", size = 194104, upload-time = "2026-08-03T21:20:41.725Z" },
{ url = "https://files.pythonhosted.org/packages/7b/1b/58496f2ed0a35de575250c02a43ab3cc2c04d494a88fed31c1cabc0fd176/cffi-2.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ad2c86c495b899d862ea0f4b42891b8713a3bd45dd4105c7fd51c2a72f39f3a5", size = 186402, upload-time = "2026-08-03T21:20:43.042Z" },
{ url = "https://files.pythonhosted.org/packages/c1/8f/9ebe220eab48a093d1a5a5e339ab0dc7316eef3bb04d63c42f0251b61f50/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:dddad92b554513a31f272570678ba307fb9f618f05e3d4a5eacafff9eae03e1d", size = 194043, upload-time = "2026-08-03T21:20:48.179Z" },
{ url = "https://files.pythonhosted.org/packages/ff/69/844bad3ece306c4782c2ecb93597035b6690d48704b803914c199da1e8b3/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:da0e573f9f97159390c89d9f1a9e41908b66d408cc5b58d08cf3847d844c531b", size = 196737, upload-time = "2026-08-03T21:20:49.457Z" },
{ url = "https://files.pythonhosted.org/packages/1b/8a/af668013284634733f02d683458a0728739c7d6ddb5e14cb0c20832266fe/cffi-2.1.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:fb92203a88b3d3053034db775110081c49d28be6551923805e039924093761e4", size = 184933, upload-time = "2026-08-03T21:20:50.639Z" },
{ url = "https://files.pythonhosted.org/packages/0c/75/2f5207ff6d1a613133b23a5203cc0c2a628313b5eb3974d7956ae3c57950/cffi-2.1.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2ae64be792b8966f2c69538199728b290e34726562896df1e5dc8ffd8d8188e8", size = 185002, upload-time = "2026-08-03T21:20:52.173Z" },
{ url = "https://files.pythonhosted.org/packages/e2/31/9e1313b0a6e30e91b3b3d3fff51ae99c857c07738e3afcce1f7334e1b7ab/cffi-2.1.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:507a24c282e0f42f8ed737cf048572cbf580468da5555764a8331735e9c736b6", size = 222271, upload-time = "2026-08-03T21:20:53.462Z" },
{ url = "https://files.pythonhosted.org/packages/50/e3/f6234a833e6e08c7007003074723c406559eecf9b48dfc97471e5a8eb7a0/cffi-2.1.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:246fa40ce8645a614ff682e0b70f37134e460eaf93a775e0cbe3cca585a67a80", size = 209919, upload-time = "2026-08-03T21:20:54.783Z" },
{ url = "https://files.pythonhosted.org/packages/0d/fc/5f74e293fced6edb51af3a46c4ccf6c23c9943774ecb375ddbd522c76add/cffi-2.1.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:471cee653ae88de62096552e6d24ccb4a5adb8c8c9f10b5054d0122c15bf2779", size = 208529, upload-time = "2026-08-03T21:20:56.066Z" },
{ url = "https://files.pythonhosted.org/packages/44/16/29e6d01b388bef055ecd6ca8244b3f4d336bd09e92d5d892187b9601084e/cffi-2.1.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aeae0e330c9f6acd681f647d46cefd30c29f93e3392882e792e82080c9691399", size = 221630, upload-time = "2026-08-03T21:20:57.336Z" },
{ url = "https://files.pythonhosted.org/packages/a4/18/fa7f1f6857d5eb88a4ca99ffcbfb7c387a287ccc154c64a73e86314745d7/cffi-2.1.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:42a494cee34437f05546455144f2b5d9ac09b1face62bcfce597d2e521066688", size = 225134, upload-time = "2026-08-03T21:20:58.675Z" },
{ url = "https://files.pythonhosted.org/packages/e0/9f/e8e3dfa04a1b4c241f8c91faacad872b4d4efd051d49764ad4e2fd4b9fea/cffi-2.1.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:cc572dace3f60ef98d7b12ff411d20f5362feb31a0439eab0085bbfd349982d7", size = 223197, upload-time = "2026-08-03T21:20:59.968Z" },
{ url = "https://files.pythonhosted.org/packages/f8/7e/8debeb04f1ab9fe2a6963964cd6f1aaf7192627b83926586a6a4e089c9fa/cffi-2.1.1-cp315-cp315-win32.whl", hash = "sha256:4f42141fc14250de6dde5ee7ea4432be017252d91f19c5ad043c084cea629cac", size = 177683, upload-time = "2026-08-03T21:21:14.901Z" },
{ url = "https://files.pythonhosted.org/packages/e0/31/5158704cc474ab65c1647932e88be78dc0873f47130e253be38bcaf13d01/cffi-2.1.1-cp315-cp315-win_amd64.whl", hash = "sha256:e6e8cff14d6fb0be70a09c0bdc58096f501952d04624ebf867e0e56da2df8960", size = 187897, upload-time = "2026-08-03T21:21:16.108Z" },
{ url = "https://files.pythonhosted.org/packages/cc/4b/b3a2da8570c704ffc0f9762cdc3ec0f02c8573798e0b5cf7f11c82bbb70f/cffi-2.1.1-cp315-cp315-win_arm64.whl", hash = "sha256:27350daa11d4f10c540e6e89dada4c54feb7256ad03e9a4dc075ebad7ba360d1", size = 182935, upload-time = "2026-08-03T21:21:17.271Z" },
{ url = "https://files.pythonhosted.org/packages/d0/ef/5443574510a1207e6f6bc38ba6e1f1de36cb48fef07b2728bb896a21f430/cffi-2.1.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:c26608d2222fb1e94487e4a387d85f13eb55d5ed725cb25a0c589ac4ee60e7bc", size = 188464, upload-time = "2026-08-03T21:21:01.163Z" },
{ url = "https://files.pythonhosted.org/packages/7e/ae/a56fa8c4686ad50e148fcbc8d3ae0d03915ff5c30d795058988c24118cef/cffi-2.1.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:4be96343e422f2dfcd12ab5c9f5aebe03f82f737c6bffeca6830b3875cb44aab", size = 188262, upload-time = "2026-08-03T21:21:02.382Z" },
{ url = "https://files.pythonhosted.org/packages/53/b2/6187f46f2912276a3ae284076109cc5c8680482f11f766ccf26db4a86427/cffi-2.1.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:937c0052c05a31ca1daf18de3158eed4dbfcb9cc107adbea227728d647be701e", size = 223779, upload-time = "2026-08-03T21:21:03.553Z" },
{ url = "https://files.pythonhosted.org/packages/8a/f6/c3ad28bd19f77047a03084424fbd4cbe997303267c14423737324be0385d/cffi-2.1.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:df423d40ee8654634421812bc3b196da3f9bd7d32929da813f8394c4348a5358", size = 211520, upload-time = "2026-08-03T21:21:04.863Z" },
{ url = "https://files.pythonhosted.org/packages/a0/cd/ccac9013a5bd9fd764de118674ab9c805b5ca10c19270d90ee273f8b2240/cffi-2.1.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a730a083190634c65cca36ba5f489531576ebd79bcd5c8e172130f6453127231", size = 210673, upload-time = "2026-08-03T21:21:06.223Z" },
{ url = "https://files.pythonhosted.org/packages/52/86/2976131c639aead931c5bee5aba67e4b09fbeb8018b6f282f70803f923a7/cffi-2.1.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:363e05fa78e15116c3c32c210ee36884fd6b9afa6d440e47112c3bd511d64cb6", size = 223835, upload-time = "2026-08-03T21:21:07.539Z" },
{ url = "https://files.pythonhosted.org/packages/ac/0c/33a7aeab2f9c76918c52e084beb39c570db3588133412929e8ec06fab90b/cffi-2.1.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:770de9db11e84213beec501cfcaa013b019820ca881e03344dea5844f7876d94", size = 226705, upload-time = "2026-08-03T21:21:08.774Z" },
{ url = "https://files.pythonhosted.org/packages/e3/26/2cde30fdde421130bfc18f70395731a6e6b2053c6a1978a5258ff04e72fa/cffi-2.1.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:7da0c5eff80f0197f3b3d1232ec5a682a9325f4ae9016a78f5f5ca35f9ced1f5", size = 225539, upload-time = "2026-08-03T21:21:09.911Z" },
{ url = "https://files.pythonhosted.org/packages/6d/cd/a361394c94b2129d604bb846f624a8e88255a3ee33129c434a00d715e64f/cffi-2.1.1-cp315-cp315t-win32.whl", hash = "sha256:06c72bb76605a4b0cd0aad6930b69d4baf7dd5d806cfc409b824191099700e66", size = 182707, upload-time = "2026-08-03T21:21:11.226Z" },
{ url = "https://files.pythonhosted.org/packages/9b/b5/ba2b299993c26577d529b6ae29841f9e15b9fcf004d65f423f4fcf94ade9/cffi-2.1.1-cp315-cp315t-win_amd64.whl", hash = "sha256:d9c275eaacd24aa73f94ffd6de08fc3f932424d8b6c376f4bed7cde376fe7bc3", size = 193772, upload-time = "2026-08-03T21:21:12.39Z" },
{ url = "https://files.pythonhosted.org/packages/aa/29/35e016098c814cd93de9cd320c66b5bfba14dc6ecedd3cb518fa7c408c69/cffi-2.1.1-cp315-cp315t-win_arm64.whl", hash = "sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692", size = 186360, upload-time = "2026-08-03T21:21:13.636Z" },
]
[[package]]
name = "charset-normalizer"
version = "3.5.1"
@@ -234,6 +341,75 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "cryptography"
version = "50.0.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9d/af/182eb91b0df3fe75c4d9f26fe70684569566745f6ba7e5c9c73a862c5252/cryptography-50.0.2.tar.gz", hash = "sha256:7b46165bb56eb4704e2eaaf86f3c940d19154535d9b0ca7d6d590b04060e00d5", size = 880623, upload-time = "2026-09-30T15:30:04.884Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e5/56/d194340cc4a57535e82e1bee9e89667ac4b7c13b5d3f59686deae3094dd5/cryptography-50.0.2-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:fa8f5efb344d6908a1ce62f4a24e2e5780f825d6f53f5f50ec5ffacac72936cb", size = 3914904, upload-time = "2026-09-30T14:43:44.339Z" },
{ url = "https://files.pythonhosted.org/packages/d9/69/c9bd862c3bf43d6399c433caf002df16e2dffd4be49bdf515cda38038711/cryptography-50.0.2-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:79def8d059362e7831389ed3be0ecdf58a89386e1271e35dd9f5af84e81bffd0", size = 4731146, upload-time = "2026-09-30T14:43:47.113Z" },
{ url = "https://files.pythonhosted.org/packages/21/69/64cef1f702bf6657e0cc186ed1a2891d50d29fb41586b254e1c07adea261/cryptography-50.0.2-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:630ebfea3bf689d075f82316324ff7433dc447fe6bc1bfc76524b74b4a9567d2", size = 4719841, upload-time = "2026-09-30T14:43:49.01Z" },
{ url = "https://files.pythonhosted.org/packages/38/6b/61a3f8d8c5e1e49a6cddccafc4015cc1c0021360ab0acb4080e7a423644a/cryptography-50.0.2-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f9f6143a8c75945eb960d9eb98905a441394abfa24afaae239d514ffb2586480", size = 4738340, upload-time = "2026-09-30T14:43:50.932Z" },
{ url = "https://files.pythonhosted.org/packages/7b/2e/7212ca32fd43dc91f2f41db20160b268098874b4c9a0e7be94d6835f5b2e/cryptography-50.0.2-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:a582ab2ae1d34f67112cadc86702774c9ea4374df6bca6afe672817203c99134", size = 5367029, upload-time = "2026-09-30T14:43:52.911Z" },
{ url = "https://files.pythonhosted.org/packages/1a/f1/b474e930c4d910328780e3940da76f5aa5cbc48ce1fc14e44d239d9ea9db/cryptography-50.0.2-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4061c0079120205fb760c58acab6443e217307dcf05e3702cf970e0689972856", size = 4753050, upload-time = "2026-09-30T14:43:55.272Z" },
{ url = "https://files.pythonhosted.org/packages/7c/52/9af10e80ac16b0fcc2123f9cbd5e7afbd0fd5075bb7a607c592258a39cda/cryptography-50.0.2-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:ac9ed99d81760c62fe89d5f0815cdfa1ba9a35141cf30f1c2d044f04b4803d2e", size = 4376724, upload-time = "2026-09-30T14:43:57.24Z" },
{ url = "https://files.pythonhosted.org/packages/71/37/6202e488cc1eb625ea110c292c6bda92823176e023f427d8d5660ce8d632/cryptography-50.0.2-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:87e9ce85beb6b328ba370cc6e6aea483c92617b4c95b1d33a49297eb662bfb04", size = 4737859, upload-time = "2026-09-30T14:43:59.541Z" },
{ url = "https://files.pythonhosted.org/packages/8f/30/e86d7d518489b0ae2497091a35287abcb1a2ce4037837a34afbe9b1d6964/cryptography-50.0.2-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:f265528741e048bce55c3463ed721fb0aa45a5888d8add8cfeccb3035451bbdc", size = 5324103, upload-time = "2026-09-30T14:44:01.901Z" },
{ url = "https://files.pythonhosted.org/packages/d3/69/2c833a049475e0a3444e94c7d0aca0aa51d166374a449b09e92ac98138de/cryptography-50.0.2-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9dab55f57c74c3cad24c323bacbbd04be4705ba6eb0d92e920b1fc4837ed5079", size = 4752576, upload-time = "2026-09-30T14:44:04.545Z" },
{ url = "https://files.pythonhosted.org/packages/6c/5d/906970b83bbfc1f5bbfb677a143c181f2801f23b6a7204a3b47c42c97e65/cryptography-50.0.2-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25784ce8b9621c90c643efb9e1e2162ab3b0224cae446ad5e70e7fcb1ce18b51", size = 4870819, upload-time = "2026-09-30T14:44:06.884Z" },
{ url = "https://files.pythonhosted.org/packages/68/e3/f2298d3bb55e0c4a91841ec4d01b3f020ba8c5fbf15ccdcc6dcf03f97025/cryptography-50.0.2-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:85d0d9a31b9098e98534226d5686b47264b95e62ce459dc2e62fdfc809f9fe93", size = 5030152, upload-time = "2026-09-30T14:44:09.443Z" },
{ url = "https://files.pythonhosted.org/packages/9a/4f/adfc442765721292fff86d314ce385d3249d22db42295c0dd057727b60f3/cryptography-50.0.2-cp311-abi3-win_amd64.whl", hash = "sha256:7afa5a6602a9f29af1f3a2965f831bae7c9d5d597b7cbb716d41ab3b7d89879c", size = 3824692, upload-time = "2026-09-30T14:44:11.671Z" },
{ url = "https://files.pythonhosted.org/packages/ce/cb/52eb3770c0d0be2702a98c6e96065ddc0a2877cf0845aa9c23397c142cd4/cryptography-50.0.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f785f6161f202ab04d8ca194158968798e480ca058943907972da5f12e2881e8", size = 3892731, upload-time = "2026-09-30T14:44:13.485Z" },
{ url = "https://files.pythonhosted.org/packages/19/8e/aa1fc533d4546b127b45de8aa024eb5933d23eff9debfe25931e56861095/cryptography-50.0.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0ecbc5652bdb6fc9eaf89a7d196e20941adfe812f43bc4ca05d9150496821047", size = 4710431, upload-time = "2026-09-30T14:44:15.427Z" },
{ url = "https://files.pythonhosted.org/packages/6a/64/72bc3f75176e7e406b748a3e3830432b8c51297b38368713df04dc04898a/cryptography-50.0.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ab50ee449bf968271e820086f10a33d101dd060370abc10bcd22279be2656539", size = 4694824, upload-time = "2026-09-30T14:44:17.69Z" },
{ url = "https://files.pythonhosted.org/packages/4e/c6/62c77550edfa5ca3f14bf44a1e6739b9fa09d6e998a11d97ed8213bccc98/cryptography-50.0.2-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:a9f7355e6fab51f6c369b86fb7571cffa05edee2c2121e0380a37fb9ac1cd5c1", size = 4716967, upload-time = "2026-09-30T14:44:19.661Z" },
{ url = "https://files.pythonhosted.org/packages/f4/37/cce70f150c432914460157a6ecc161752e053aa5ec0ef3b3f7dc6e31039a/cryptography-50.0.2-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:94e5e9f108ee10471288214d3d233fbfbb492840a8457eb85178d643ddeb32c7", size = 5328676, upload-time = "2026-09-30T14:44:21.744Z" },
{ url = "https://files.pythonhosted.org/packages/aa/9a/6f2f0304d634ceafdeaf23e84537336664ac419b5d07611675c2ad3f6b7a/cryptography-50.0.2-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:241449bf940a5d27309bd317e6f9a2af6932113818bb2b8f5c59ddc7ef16da18", size = 4727698, upload-time = "2026-09-30T14:44:24.178Z" },
{ url = "https://files.pythonhosted.org/packages/1d/de/66bcf9244d118663b2e1aaded8990f4640e3d7b7411870a5765f252074d2/cryptography-50.0.2-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d8947001be83df1394050758ce0e745dd74fb134eef0a4b5124208dfc3a68c37", size = 4354821, upload-time = "2026-09-30T14:44:26.263Z" },
{ url = "https://files.pythonhosted.org/packages/bd/e6/db28a28c7b6c676addce89136de3d8db49ea825a8c863472e36e42ead4ad/cryptography-50.0.2-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4a20ce1e5cb4284a86692fdcba7cb8754185c6b2e5c56fcef3751cf451d3cdc2", size = 4716748, upload-time = "2026-09-30T14:44:28.447Z" },
{ url = "https://files.pythonhosted.org/packages/30/96/01546c7f69ea0e2ab790a2e4f0934a4052fb9b388147fbf83c2fd72f1e57/cryptography-50.0.2-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:84f964e537f916e2cc85199e5a88742e964939b575ac8598b3f9d6cc416cdaf1", size = 5285085, upload-time = "2026-09-30T14:44:30.704Z" },
{ url = "https://files.pythonhosted.org/packages/6c/01/03263395f74d50b071e9e66daace3f8bef80493e5d410726f2ba8554736b/cryptography-50.0.2-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:828d49b0ff5a0e3975865571c5d91dbbdd0d38d8289b249a163e9425413a5e05", size = 4727268, upload-time = "2026-09-30T14:44:32.92Z" },
{ url = "https://files.pythonhosted.org/packages/eb/94/2bfe8f29ec0cc9c0d99359c4161adf32858e4934b72c6d100d2ac0bbe962/cryptography-50.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:deb9fde5c60e437ee4821bc9bc39ff31b42135c27e1dc61ef0a629389c1de62e", size = 4849503, upload-time = "2026-09-30T14:44:34.969Z" },
{ url = "https://files.pythonhosted.org/packages/54/44/e80651ecbf0e42b62e2bb5f5768916e07eea72e1297338956a61df361f88/cryptography-50.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8c71ba2cd31fc93748c38e1b613200ff1c2665cbfd5341fe3a61cfde35a1430e", size = 5004057, upload-time = "2026-09-30T14:44:37.064Z" },
{ url = "https://files.pythonhosted.org/packages/f8/cc/1d33befb3cd7ea7e77d2d73f43f2066471da1b21f24a6156efcaabf6d2e8/cryptography-50.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:78198641e5be9521beea5aa782bb551a58068d10e6eb04c9c680c1b69f2e7d45", size = 3795868, upload-time = "2026-09-30T14:44:39.71Z" },
{ url = "https://files.pythonhosted.org/packages/2d/49/93f6a6e7a87c9aa68d44d3e1cdb5fe8f60c90d5d2f46acae9a56892816b8/cryptography-50.0.2-cp315-abi3.abi3t-macosx_11_0_arm64.whl", hash = "sha256:edc3342adf8f697fc5f59c887a304356f147b397809440ed64e2fa6af2f50f37", size = 4133708, upload-time = "2026-09-30T14:44:41.807Z" },
{ url = "https://files.pythonhosted.org/packages/8c/75/32ac2a56243d778805c16ca6a32b8f74fb757df7e28d7ecb560afafb59cf/cryptography-50.0.2-cp315-abi3.abi3t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d370b8d1dfcdf7130178137f6fbee6140774a1acc6cacefc4b42643ec11d0a3a", size = 4956267, upload-time = "2026-09-30T14:44:43.693Z" },
{ url = "https://files.pythonhosted.org/packages/aa/a4/2c8d734e43d97f0842ee9f1b7b4bfb3d0cf5e19edebf43c2afe6675c2320/cryptography-50.0.2-cp315-abi3.abi3t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f2f9bd7f90c64fe89253f0a2c05e3c4856072660429ce8831b4235bf29403a67", size = 4966465, upload-time = "2026-09-30T14:44:45.769Z" },
{ url = "https://files.pythonhosted.org/packages/c2/58/ee288c829a6f41f6235ae9dd33d82fd19b45442b65b4c8a3da36963d9f7a/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_aarch64.whl", hash = "sha256:e275096ea1e60cc595cda2836fd4a6c725d1125108b868be17f53684d164e2cc", size = 4959356, upload-time = "2026-09-30T14:44:48.211Z" },
{ url = "https://files.pythonhosted.org/packages/92/20/9ded6d51ddd9897f6b6e81fb9ebea7951d7cc5d6c890b0ed8abf77a51a80/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_ppc64le.whl", hash = "sha256:b13478603dcd0a2479ff8e87e2c19a7d525734686fe3c49542472293a204212d", size = 5548822, upload-time = "2026-09-30T14:44:50.86Z" },
{ url = "https://files.pythonhosted.org/packages/02/a8/8df951850d6b31d2a00218f19e2b3f999523437ed7a819df7fa427942fca/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_28_x86_64.whl", hash = "sha256:58a0c478eeca76fe5e07993c5a0703def34a6dc6a0cda4f5564639b33112ffe7", size = 5001199, upload-time = "2026-09-30T14:44:53.379Z" },
{ url = "https://files.pythonhosted.org/packages/8b/f9/36b3022218ce75b7cdf068fb95f809f9bd0d820e4955ef43b90c255cc7ac/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_31_armv7l.whl", hash = "sha256:d38cdff612d06fa6a32840d5e1b1f7a27cee4a349aa9085d94a67789d6bfd408", size = 4629333, upload-time = "2026-09-30T14:44:55.635Z" },
{ url = "https://files.pythonhosted.org/packages/8c/72/20f99a219f6af47cdd1cbd978c243b92d71496e168a746138af44ded4f29/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_aarch64.whl", hash = "sha256:fdd28f912fccfec1846a94e2e1e8f9b0012f557f0c46fe4f3eb0d7a87afcf90b", size = 4958822, upload-time = "2026-09-30T14:44:59.639Z" },
{ url = "https://files.pythonhosted.org/packages/f2/20/196f112617fb08eb4d608a2a6c422373d46f9cc2857f38fc0667033c0899/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_ppc64le.whl", hash = "sha256:cbc8738fd8526d80f35cb3a40d41f41a2e7030bb3b18b09a6778ef63d291c2fd", size = 5506351, upload-time = "2026-09-30T14:45:02.267Z" },
{ url = "https://files.pythonhosted.org/packages/24/95/83378121ef3eaaaf71d4b781577ff794acb39b9e1b87a3f156898c8497ed/cryptography-50.0.2-cp315-abi3.abi3t-manylinux_2_34_x86_64.whl", hash = "sha256:e105ab60406787da31fccc883fc0f733af1efd78f0136a4599692c4083a73d0c", size = 5000859, upload-time = "2026-09-30T14:45:05.009Z" },
{ url = "https://files.pythonhosted.org/packages/22/f7/70fd7ae4d1dbfa7ba29b02e1b9068771519a86027756510b700ce81086a8/cryptography-50.0.2-cp315-abi3.abi3t-musllinux_1_2_aarch64.whl", hash = "sha256:6f8700550aa1474a91e5dc07049c46f98b423b5b1ddd0483e0b51362eeeaf5be", size = 5092151, upload-time = "2026-09-30T15:29:15.932Z" },
{ url = "https://files.pythonhosted.org/packages/d4/be/688367b74de86984bd58d8efacfc7c9e68b89a6a22ced0fb4f38db50254a/cryptography-50.0.2-cp315-abi3.abi3t-musllinux_1_2_x86_64.whl", hash = "sha256:c71be1cbfa5cd9a41ee452acf1eccd82b2c05950358b106ec8ceb83411d1a020", size = 5286120, upload-time = "2026-09-30T15:29:18.309Z" },
{ url = "https://files.pythonhosted.org/packages/39/d1/55f8a3f2ef5d1529e16835ef10cf0fe3d559ce237b46dddc440c0bba3649/cryptography-50.0.2-cp315-abi3.abi3t-win_amd64.whl", hash = "sha256:c423ab384a46c4dff7217b2ea5ba2e11cffdeab6441acd04cf65a369caf0366c", size = 4111557, upload-time = "2026-09-30T15:29:20.155Z" },
{ url = "https://files.pythonhosted.org/packages/23/ad/ac987755d00e1e64273760228d2635ae38dae2be83e3c6e0d3289d91dec3/cryptography-50.0.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:0ec5f09541743261e66e291b4a0cbf0fb2997aeaab6d9e9c740b9dba1b58d1c2", size = 3943588, upload-time = "2026-09-30T15:29:22.265Z" },
{ url = "https://files.pythonhosted.org/packages/d5/8d/6d585339bedf85d45044c85d8412dac53f2bb6f918e8b7777efba1787844/cryptography-50.0.2-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c5e67125c7dca78d199ec4e116aa93dbb83494808ecbb8211a2cb09b1bf41dbd", size = 4756166, upload-time = "2026-09-30T15:29:24.58Z" },
{ url = "https://files.pythonhosted.org/packages/bf/f1/1c1f6874e8550cfddd4b688ceb38cefb6ed15ceed224d56f133f3d88c214/cryptography-50.0.2-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee247f5c245c9a2fe7c8e2214e295918838e44e00a45a6718451e4004219e767", size = 4749145, upload-time = "2026-09-30T15:29:26.807Z" },
{ url = "https://files.pythonhosted.org/packages/c1/63/61b15dc1a8de03fe0adbe3fd7608b3ad5c73bf50993bbcb1faaa930afe33/cryptography-50.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dfe9763530994147d9af1def057a5b9658b00e8f8fe8743d144d1e0911c2e454", size = 4763638, upload-time = "2026-09-30T15:29:28.588Z" },
{ url = "https://files.pythonhosted.org/packages/fc/35/b345bdfa40c9126df1a9d33236aa98418367931b8725f84fc3ae2b98dc59/cryptography-50.0.2-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:58ddb5a8e3179d12f19e4ea34d2d32e9d63a4baa142c875c1eb59f41b7243acd", size = 5382217, upload-time = "2026-09-30T15:29:30.589Z" },
{ url = "https://files.pythonhosted.org/packages/4f/87/ef344a9e616871f2519c22d6afcda79ddd5d35e9592d95eb6e677608d055/cryptography-50.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f21e8a22c8605750c7af886bab299a363721264061b4ac0a30efb73cfd58efc5", size = 4781387, upload-time = "2026-09-30T15:29:32.605Z" },
{ url = "https://files.pythonhosted.org/packages/90/5b/f2fdb13cd0b96f6f932c8627bb292a45f11c64d21620a8e120aee9a3b848/cryptography-50.0.2-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:9c8402a82ea0dc4ceeab793db05f0fafa8ca139ca34fcde5df0f596103c74107", size = 4403790, upload-time = "2026-09-30T15:29:34.374Z" },
{ url = "https://files.pythonhosted.org/packages/bc/ce/7e4f662b1e3c393513569e402cfc85ac7da0bd3d5435e122a3140219eb2d/cryptography-50.0.2-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:0ddc924c04591c2811ca024d62ecad4f7f6f08af8939c211438f48a16bd23602", size = 4764319, upload-time = "2026-09-30T15:29:36.149Z" },
{ url = "https://files.pythonhosted.org/packages/3c/3f/86ff33ce34cc0de6847fb96e035a1a760d81652e38643f617c02ad32ef7a/cryptography-50.0.2-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:a6557e5f38e065ca9fbdaf7cfc7435ecb1d113aa81a022d1b51921ee7432e227", size = 5338560, upload-time = "2026-09-30T15:29:39.053Z" },
{ url = "https://files.pythonhosted.org/packages/40/cf/6b5c8e2fd9202d98988ab7cb5cc5c991704c4ad55f492ff408e4969f83f1/cryptography-50.0.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:1981f1db4630889b9ef7803fadef12b056f428cb6b85c27ba57b774793b6093c", size = 4780973, upload-time = "2026-09-30T15:29:41.251Z" },
{ url = "https://files.pythonhosted.org/packages/10/bf/8d6ebc7dded797bd0f0160d52188021211f011a2b164ef0ae1dac4587465/cryptography-50.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7a8701d6b584d76e909e3d305b7d126b41439876a5aaf76cddc67fc230eafa2e", size = 4897738, upload-time = "2026-09-30T15:29:43.106Z" },
{ url = "https://files.pythonhosted.org/packages/d4/aa/f3f6e0de7e6253b8baa8b2d8fb9d50924fa75cee3d4624bd4bc1208ee923/cryptography-50.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ce47f66801c20ec6c6632453bb5960fe38939e9306970b48b3a5a26de7745d94", size = 5058280, upload-time = "2026-09-30T15:29:44.827Z" },
{ url = "https://files.pythonhosted.org/packages/f6/b6/a1faf3a27ae9405fb34b1713cc73b2d8a26b04d5c561578fa2e6ef3e5bb9/cryptography-50.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:4e81d95e5bafc2d6e34e4bed780e53e4d5b9a2f928573428aa4d35fbec1eb0de", size = 3854095, upload-time = "2026-09-30T15:29:46.782Z" },
{ url = "https://files.pythonhosted.org/packages/1d/7a/f08d34ce09d60f89ebd391e2ebc6ba2b995e6dd7552f41820f8085f94e53/cryptography-50.0.2-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:92e665960f25fcdc73725b9cec7a3824f279ba97a98653afe9ffac2e43668f67", size = 4716035, upload-time = "2026-09-30T15:29:48.681Z" },
{ url = "https://files.pythonhosted.org/packages/45/67/e18fb65592451a2acb76e9f2fbe14e0f47a8318b4c5430f1633851d03daa/cryptography-50.0.2-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:eef4c2f3423810b3070ab391f85436d2f8bbfcb286ac15cbc73190b3563b1f1a", size = 4726917, upload-time = "2026-09-30T15:29:50.608Z" },
{ url = "https://files.pythonhosted.org/packages/83/28/38fdce17e60f6b825e69fc3b7f75e70a6612759980704697e1de4cbfaf6e/cryptography-50.0.2-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:7c6d0330c472d96f6a6afe24d80dfdf15176c33096f0a4397ae4c60f3dd3be48", size = 4715341, upload-time = "2026-09-30T15:29:52.522Z" },
{ url = "https://files.pythonhosted.org/packages/b6/b1/d9121a717e0f893c64bd6ca7702614778d7df2a5c309128a002421788516/cryptography-50.0.2-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:1ba34f04897fcdaa73f74145c25f3ec146fbd56593853e88adc2e811303c5f42", size = 4726322, upload-time = "2026-09-30T15:29:54.263Z" },
{ url = "https://files.pythonhosted.org/packages/36/8b/e6d153808bf353e152abd2fd4d8f09670d956ac78379ac46e60d7efbf04c/cryptography-50.0.2-pp311-pypy311_pp80-macosx_11_0_arm64.whl", hash = "sha256:3dc4fd8058cea1644971207d530e1a03a184a805ffc8ebdddf0599d78a331b81", size = 3873097, upload-time = "2026-09-30T15:29:56.097Z" },
{ url = "https://files.pythonhosted.org/packages/ca/1d/1271f287ff7170ddafc2aad36260c4eec20ccd2fea70f38455e9d56d427b/cryptography-50.0.2-pp311-pypy311_pp80-win_amd64.whl", hash = "sha256:7b75de3c8b3be1cdb1052747c929440c3eea46c1bc2cb8a6e3a48388e9b7b452", size = 3805376, upload-time = "2026-09-30T15:29:58.729Z" },
]
[[package]]
name = "detect-installer"
version = "0.2.1"
@@ -582,6 +758,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
]
[[package]]
name = "httpx-sse"
version = "0.4.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/0f/4c/751061ffa58615a32c31b2d82e8482be8dd4a89154f003147acee90f2be9/httpx_sse-0.4.3.tar.gz", hash = "sha256:9b1ed0127459a66014aec3c56bebd93da3c1bc8bb6618c8082039a44889a755d", size = 15943, upload-time = "2025-10-10T21:48:22.271Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d2/fd/6668e5aec43ab844de6fc74927e155a3b37bf40d7c3790e49fc0406b6578/httpx_sse-0.4.3-py3-none-any.whl", hash = "sha256:0ac1c9fe3c0afad2e0ebb25a934a59f4c7823b60792691f779fad2c5568830fc", size = 8960, upload-time = "2025-10-10T21:48:21.158Z" },
]
[[package]]
name = "httpx2"
version = "2.13.0"
@@ -763,6 +948,33 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/9e/6a/a83720e953b1682d2d109d3c2dbb0bc9bf28cc1cbc205be4ef4be5da709d/jsonpointer-3.1.1-py3-none-any.whl", hash = "sha256:8ff8b95779d071ba472cf5bc913028df06031797532f08a7d5b602d8b2a488ca", size = 7659, upload-time = "2026-03-23T22:32:31.568Z" },
]
[[package]]
name = "jsonschema"
version = "4.26.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "attrs" },
{ name = "jsonschema-specifications" },
{ name = "referencing" },
{ name = "rpds-py" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b3/fc/e067678238fa451312d4c62bf6e6cf5ec56375422aee02f9cb5f909b3047/jsonschema-4.26.0.tar.gz", hash = "sha256:0c26707e2efad8aa1bfc5b7ce170f3fccc2e4918ff85989ba9ffa9facb2be326", size = 366583, upload-time = "2026-01-07T13:41:07.246Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/69/90/f63fb5873511e014207a475e2bb4e8b2e570d655b00ac19a9a0ca0a385ee/jsonschema-4.26.0-py3-none-any.whl", hash = "sha256:d489f15263b8d200f8387e64b4c3a75f06629559fb73deb8fdfb525f2dab50ce", size = 90630, upload-time = "2026-01-07T13:41:05.306Z" },
]
[[package]]
name = "jsonschema-specifications"
version = "2025.9.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "referencing" },
]
sdist = { url = "https://files.pythonhosted.org/packages/19/74/a633ee74eb36c44aa6d1095e7cc5569bebf04342ee146178e2d36600708b/jsonschema_specifications-2025.9.1.tar.gz", hash = "sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d", size = 32855, upload-time = "2025-09-08T01:34:59.186Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/41/45/1a4ed80516f02155c51f51e8cedb3c1902296743db0bbc66608a0db2814f/jsonschema_specifications-2025.9.1-py3-none-any.whl", hash = "sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe", size = 18437, upload-time = "2025-09-08T01:34:57.871Z" },
]
[[package]]
name = "langchain"
version = "1.4.2"
@@ -816,6 +1028,20 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e6/18/cf18cfec118b02e003f35300b54d6a1173dc6e4905cffdf5ea49e390a7f9/langchain_core-1.6.3-py3-none-any.whl", hash = "sha256:114fe1868c9ed60606662b9dd0074bf3152cc6b0b1522ba37cbd6b04f55ca77d", size = 571753, upload-time = "2026-09-11T17:37:43.875Z" },
]
[[package]]
name = "langchain-mcp-adapters"
version = "0.3.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "langchain-core" },
{ name = "mcp" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/05/49/f3b8497b64024ab50d10011f27e94d149668fef754da74c1c2ce6ebe4a30/langchain_mcp_adapters-0.3.2.tar.gz", hash = "sha256:61cd1a09597adb619a9bafb0642938ffc2a9463d699a753f7af0420ea46c381a", size = 47129, upload-time = "2026-08-06T06:15:04.094Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a3/5e/4f117d2500a661079a1895a6eb18954a906e458b1e45fa04a301fcdabd61/langchain_mcp_adapters-0.3.2-py3-none-any.whl", hash = "sha256:094e6b3096dbcc408417d5722f6915f164772e50c502ae3d8989405bf12c3c84", size = 28879, upload-time = "2026-08-06T06:15:02.832Z" },
]
[[package]]
name = "langchain-mongodb"
version = "0.12.1"
@@ -1102,6 +1328,31 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" },
]
[[package]]
name = "mcp"
version = "1.30.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "httpx" },
{ name = "httpx-sse" },
{ name = "jsonschema" },
{ name = "pydantic" },
{ name = "pydantic-settings" },
{ name = "pyjwt", extra = ["crypto"] },
{ name = "python-multipart" },
{ name = "pywin32", marker = "sys_platform == 'win32'" },
{ name = "sse-starlette" },
{ name = "starlette" },
{ name = "typing-extensions" },
{ name = "typing-inspection" },
{ name = "uvicorn", marker = "sys_platform != 'emscripten'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ba/93/0142dc84a666daf8ad51a34268f34c12fd6fda4f3810c4be2504eecc8212/mcp-1.30.0.tar.gz", hash = "sha256:445414625fce5c295faa505bb11bacece661ab6f4028d57c935db57820b7a3e4", size = 680511, upload-time = "2026-09-07T14:34:15.845Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f5/f4/e58bc33317c92a0203664daaf00bf6f41166cc0149e5d6870a03f7cd004a/mcp-1.30.0-py3-none-any.whl", hash = "sha256:666edb5009503e1047c9d60346a756f94b261f05cc2625f23d41c728ffc484d0", size = 234581, upload-time = "2026-09-07T14:34:14.266Z" },
]
[[package]]
name = "mdurl"
version = "0.1.2"
@@ -1119,11 +1370,13 @@ dependencies = [
{ name = "fastapi", extra = ["standard"] },
{ name = "httpx" },
{ name = "langchain" },
{ name = "langchain-mcp-adapters" },
{ name = "langchain-openai" },
{ name = "langfuse" },
{ name = "langgraph" },
{ name = "langgraph-checkpoint-mongodb" },
{ name = "loguru" },
{ name = "mcp" },
{ name = "pydantic" },
{ name = "pymongo" },
{ name = "python-dotenv" },
@@ -1136,11 +1389,13 @@ requires-dist = [
{ name = "fastapi", extras = ["standard"], specifier = ">=0.141.1" },
{ name = "httpx", specifier = ">=0.28.1" },
{ name = "langchain", specifier = ">=1.4.2" },
{ name = "langchain-mcp-adapters", specifier = ">=0.3.0" },
{ name = "langchain-openai", specifier = ">=1.6.2" },
{ name = "langfuse", specifier = "==4.15.6" },
{ name = "langgraph", specifier = ">=1.2.12" },
{ name = "langgraph-checkpoint-mongodb", specifier = ">=0.5.0" },
{ name = "loguru", specifier = ">=0.7.3" },
{ name = "mcp", specifier = ">=1.28.1" },
{ name = "pydantic", specifier = ">=2.13.5,<3" },
{ name = "pymongo", specifier = ">=4.17.0" },
{ name = "python-dotenv", specifier = ">=1.2.3" },
@@ -1574,6 +1829,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e4/04/d52c7016b04b6c5108f26691f9d33ec82a9b65d041f1a9c771137693d618/protobuf-7.36.2-py3-none-any.whl", hash = "sha256:bdb3a345d48db958e6ce1f18e508beb0cc981d64f24088427549c866cd039f1e", size = 179806, upload-time = "2026-09-17T20:07:58.211Z" },
]
[[package]]
name = "pycparser"
version = "3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
]
[[package]]
name = "pydantic"
version = "2.13.5"
@@ -1732,6 +1996,20 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" },
]
[[package]]
name = "pyjwt"
version = "2.15.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/43/ea/5194e52748b0da83d71e082d75496eaec6e58f419f5e184786ded517e6a9/pyjwt-2.15.1.tar.gz", hash = "sha256:4f259e80cdfb6b3fc18a7de51fd1ef9ec79652f25019bae68975ca2468a34df8", size = 121252, upload-time = "2026-09-28T18:40:42.598Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/50/ca/44de4e75f8aadc457f0634be3b542815078ded46dca30efb960edeecad6e/pyjwt-2.15.1-py3-none-any.whl", hash = "sha256:42d59d631f7768a1028a64c7ff581a9bf7519804daf91fc5b6c56e30eec5e193", size = 33860, upload-time = "2026-09-28T18:40:41.429Z" },
]
[package.optional-dependencies]
crypto = [
{ name = "cryptography" },
]
[[package]]
name = "pymongo"
version = "4.17.0"
@@ -1823,6 +2101,28 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/e1/04/e8135ebd1ad02c56ec633277529b2602ff99ff634be76cdba5744cf554fd/python_multipart-0.0.32-py3-none-any.whl", hash = "sha256:ff6d3f776f16878c894e52e107296ffc890e913c611b1a4ec6c44e2821fe2e23", size = 30042, upload-time = "2026-06-04T16:18:57.319Z" },
]
[[package]]
name = "pywin32"
version = "312"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1f/f5/10a6e845a00fc5e7afd0a988b744f403d4d57162a28d160a093c4d9322f0/pywin32-312-cp311-cp311-win32.whl", hash = "sha256:17948aeadbdb091f0ced6ef0841620794e68327b94ee415571c1203594b7215c", size = 6362659, upload-time = "2026-06-04T07:49:21.349Z" },
{ url = "https://files.pythonhosted.org/packages/35/c4/dcd2d62b5944b6d5db53413a5899016ccd57ffcb7278f3f81655d25d2027/pywin32-312-cp311-cp311-win_amd64.whl", hash = "sha256:d11417d84412f859b722fad0841b3614459ed0047f7542d8362e77884f6b6e8a", size = 6928825, upload-time = "2026-06-04T07:49:23.934Z" },
{ url = "https://files.pythonhosted.org/packages/b7/56/3cbb433fe4501cdba2eb9040f56a4e1a8243faa4186b25295564d1a7a79d/pywin32-312-cp311-cp311-win_arm64.whl", hash = "sha256:b2200a054ca6d6625c4842fc56a4976a4b47f96b73dbe5538c3f813a80359f47", size = 6721875, upload-time = "2026-06-04T07:49:26.416Z" },
{ url = "https://files.pythonhosted.org/packages/83/ff/32aa7d2ed0ab12b323aaa64f9b75e6ad4f8fd09f9ccfc28c79414d46838d/pywin32-312-cp312-cp312-win32.whl", hash = "sha256:dab4f65ac9c4e48400a2a0530c46c3c579cd5905ecd11b80692373915269208b", size = 6371877, upload-time = "2026-06-04T07:49:28.836Z" },
{ url = "https://files.pythonhosted.org/packages/03/d9/77040d3b43df3f3be32ea289433d660d2727f5ba327bc73be835127d9d60/pywin32-312-cp312-cp312-win_amd64.whl", hash = "sha256:b457f6d628a47e8a7346ce22acb7e1a46a4a78b52e1d17e1af56871bd19a93bc", size = 6914841, upload-time = "2026-06-04T07:49:31.85Z" },
{ url = "https://files.pythonhosted.org/packages/e3/cc/7b1ec671775756020a0ee7f4feeaf3c568f0ab86bd3900088cf986937a92/pywin32-312-cp312-cp312-win_arm64.whl", hash = "sha256:6017c58e12f6809fbb0555b75df144c2922a9ffd18e4b9b5afa863b6c1a9d950", size = 6727901, upload-time = "2026-06-04T07:49:34.244Z" },
{ url = "https://files.pythonhosted.org/packages/2d/41/12fbfd7f36ed2146d8bc9de96c2741296bf0d490b98508496cff322e274c/pywin32-312-cp313-cp313-win32.whl", hash = "sha256:7a27df850933d16a8eabfbaeb73d52b273e2da667f80d70b01a89d1f6828d02c", size = 6370184, upload-time = "2026-06-04T07:49:36.253Z" },
{ url = "https://files.pythonhosted.org/packages/ba/db/36a78e3403099d31d9746d13fdcde5accc43c1155f375a34d15983a479a7/pywin32-312-cp313-cp313-win_amd64.whl", hash = "sha256:c53e878d15a1c44788082bfe712a905433473aa38f86375b7cf8b45e3acbaaf9", size = 6914298, upload-time = "2026-06-04T07:49:38.876Z" },
{ url = "https://files.pythonhosted.org/packages/84/37/c1697194092b76de9ed47ca124323f02c57ffc8a45c06f88a3d5acaf01eb/pywin32-312-cp313-cp313-win_arm64.whl", hash = "sha256:59aba5d5940842075343a5ddc6b11f1cdf0d1567fe745290359dfbcc7c2eb831", size = 6727640, upload-time = "2026-06-04T07:49:41.083Z" },
{ url = "https://files.pythonhosted.org/packages/fc/2b/1f3cded5822fd49c02f40544cbb5f58c7cfd6b1694869fd476cb6170ee97/pywin32-312-cp314-cp314-win32.whl", hash = "sha256:a77a90fbb6881238d2ca9c6fd797b25817f3768fe78d214a90137ff055a75f5b", size = 6468928, upload-time = "2026-06-04T07:49:43.188Z" },
{ url = "https://files.pythonhosted.org/packages/21/82/3bf86d2e2808902013132e1ce905a7da0da53790f3836c64bf44d55e24f3/pywin32-312-cp314-cp314-win_amd64.whl", hash = "sha256:a4dd3a848290ef724347b19f301045831d8e802fa4464f491b98b1e0a081432e", size = 7024157, upload-time = "2026-06-04T07:49:45.34Z" },
{ url = "https://files.pythonhosted.org/packages/a4/0e/73f6d6800b4f27655abd9e9f6aaeaefcddb2b946e4674efa2bab184a7f7b/pywin32-312-cp314-cp314-win_arm64.whl", hash = "sha256:9fce94568364e0155e6dfb781ac5d95903be8baf28670632beab1b523f300daa", size = 6839598, upload-time = "2026-06-04T07:49:47.613Z" },
{ url = "https://files.pythonhosted.org/packages/eb/61/caa39686032d2ebdd04ff0ab5cbe163126c0066d98e00c9018646e42393b/pywin32-312-cp315-cp315-win32.whl", hash = "sha256:5c1fbe4a937a73ae9297384a3da38518cbc694c68ad8a809b2e19acd350f03ed", size = 6471159, upload-time = "2026-06-04T07:49:50.035Z" },
{ url = "https://files.pythonhosted.org/packages/0f/cd/7e1de64a4a6f69c04214169657ccab0d93a670ea50e35eb8f489d7378249/pywin32-312-cp315-cp315-win_amd64.whl", hash = "sha256:c2f03a0f73f804a13c2735b99392b0cd426bb4f2c4d0178e5ac966a0f21618d5", size = 7025293, upload-time = "2026-06-04T07:49:54.857Z" },
{ url = "https://files.pythonhosted.org/packages/23/ed/4532e9388e65fa16b46776ef47ad631a64eda1631884488af707666350ed/pywin32-312-cp315-cp315-win_arm64.whl", hash = "sha256:a8597d28f267b39074aef51fa593530082b39cbe5a074226096857b1fed2dfb9", size = 6840337, upload-time = "2026-06-04T07:49:57.531Z" },
]
[[package]]
name = "pyyaml"
version = "6.0.3"
@@ -1878,6 +2178,20 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
]
[[package]]
name = "referencing"
version = "0.37.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "attrs" },
{ name = "rpds-py" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/22/f5/df4e9027acead3ecc63e50fe1e36aca1523e1719559c499951bb4b53188f/referencing-0.37.0.tar.gz", hash = "sha256:44aefc3142c5b842538163acb373e24cce6632bd54bdb01b21ad5863489f50d8", size = 78036, upload-time = "2025-10-13T15:30:48.871Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/2c/58/ca301544e1fa93ed4f80d724bf5b194f6e4b945841c5bfd555878eea9fcb/referencing-0.37.0-py3-none-any.whl", hash = "sha256:381329a9f99628c9069361716891d34ad94af76e461dcb0335825aecc7692231", size = 26766, upload-time = "2025-10-13T15:30:47.625Z" },
]
[[package]]
name = "regex"
version = "2026.9.10"
@@ -2185,6 +2499,129 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d8/de/d2c11ed977298cee41c23b6625da9f816ba9f7b91005dfb6674cc0bec09c/rignore-0.8.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:f7b929db3f8f3fa240b0f328453de0b1b747e51b7992a9198c8412d16f4f0027", size = 1144578, upload-time = "2026-08-04T22:27:06.473Z" },
]
[[package]]
name = "rpds-py"
version = "2026.6.3"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/aa/2a/9618a122aeb2a169a28b03889a2995fe297588964333d4a7d67bdf46e147/rpds_py-2026.6.3.tar.gz", hash = "sha256:1cebd1337c242e4ec2293e541f712b2da849b29f48f0c293684b71c0632625d4", size = 64051, upload-time = "2026-06-30T07:17:53.009Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/94/1f/a2dca5ffdbf1d475ffc4e80e4d5d720ff3a00f691795910116960ee12511/rpds_py-2026.6.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:7b689145a1485c335569bd056464f3243a29af7ed3871c7be31ad624ba239bc7", size = 342174, upload-time = "2026-06-30T07:14:54.821Z" },
{ url = "https://files.pythonhosted.org/packages/4d/dc/323d08583c0832911768663d1944f0107fcd4088704858d84b5e06d105a0/rpds_py-2026.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:db08f45aecde626498fb3df07bcf6d2ec040af42e859a4f5040d79c200342911", size = 345513, upload-time = "2026-06-30T07:14:56.515Z" },
{ url = "https://files.pythonhosted.org/packages/0b/2a/e31989834d18d2f26ec1d2774c5b1eb3331df4ea8ada525175294c94b48a/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acc992ab27b15f852c76755eb2ab7dce86585ddadba6fa5946e58556088845b4", size = 373783, upload-time = "2026-06-30T07:14:57.736Z" },
{ url = "https://files.pythonhosted.org/packages/87/fe/e80107ee3639585c9941c17d6a42cd65325022f656c023191fce78c324c8/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f88d653e7b3b779d71ae7454e20dcc9b6bae903f33c269db9f2be41bda3f261", size = 378316, upload-time = "2026-06-30T07:14:59.077Z" },
{ url = "https://files.pythonhosted.org/packages/22/6f/81e3adf81acfb6fa694de2a6e4e7d8863121e3e0799e0a7725e6cf5679c4/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e52655eaf81e32593abedaa4bfe33170c8cfedf3365ed9be6e11e07f148f0278", size = 499423, upload-time = "2026-06-30T07:15:00.488Z" },
{ url = "https://files.pythonhosted.org/packages/2d/9a/41263969df0ce3d9af2a96d5005a288200af1989aed3354bfceb5fc0b21f/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dfcc8b909769d19db55c7cc9541eb64b9b774b1057ffffb4f1048070475bb9f9", size = 386077, upload-time = "2026-06-30T07:15:01.911Z" },
{ url = "https://files.pythonhosted.org/packages/5e/19/7e98f468bd50346faff5b10e5297374b443bfdddacc8e9fbc65984539597/rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c1255b302953c86a486b81d330d5ee1d5bd937691ce271b6be0ef0e299eaab7", size = 371315, upload-time = "2026-06-30T07:15:03.317Z" },
{ url = "https://files.pythonhosted.org/packages/99/3c/2b973b4d371906a134b03decfea7f5d9835a2c6d263454392e15b64b5b18/rpds_py-2026.6.3-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:8d2294a31386bfa251d8c8a39472beee17db67d4f1a6eabea665d35c9a4461c3", size = 383502, upload-time = "2026-06-30T07:15:04.627Z" },
{ url = "https://files.pythonhosted.org/packages/98/2a/12e2799500af0a307bca76b63361c51f9fe479223561489c29eea1f2ee41/rpds_py-2026.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8f23ead891a3b762f35ab3b04623da7056545b48aa60d59957e6789914545da", size = 402673, upload-time = "2026-06-30T07:15:05.856Z" },
{ url = "https://files.pythonhosted.org/packages/2d/e3/21e5872d165fe08be4f229e3d5ee9d90019c0bf0e5538de60dbd54009450/rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:421aba32367055614287a4292b6a17f1939c9452299f7a0209c117e990b646d4", size = 549964, upload-time = "2026-06-30T07:15:07.159Z" },
{ url = "https://files.pythonhosted.org/packages/1a/d0/5ee0fe36844297de8123bee27bc12078c1a7416ad9f1b8a8ca18d6b0c0ac/rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1e5822dfc2f0d4ab7e745eaa6d85945069329beeccef965af3f3bb26058fcab6", size = 615446, upload-time = "2026-06-30T07:15:08.531Z" },
{ url = "https://files.pythonhosted.org/packages/b1/80/1ea5873cb683f2fbe5f21b23ea1f6d179ead19f3c5b249b7eb5dca568ef2/rpds_py-2026.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:83e35b57523816c8613fd0776b40cd8bb9f596b37ddd2692eb4a6bb5ab2f8c93", size = 576975, upload-time = "2026-06-30T07:15:09.97Z" },
{ url = "https://files.pythonhosted.org/packages/c9/e1/90ef639217a5ddb15b7f4f61b1c33911fd044ad03c311bafdd2bcab85582/rpds_py-2026.6.3-cp311-cp311-win32.whl", hash = "sha256:de3eceba0b683bcbb1ab93da016d0270df1f9ae7be716b40214c5dafac6ea45a", size = 204453, upload-time = "2026-06-30T07:15:11.324Z" },
{ url = "https://files.pythonhosted.org/packages/f2/b7/b7a1695d7af36f521fb11e80d6d3adbd744f73b921859bd3c2a2c0dc706f/rpds_py-2026.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:2c54a076ca4d370980ab57bc0e31df57bbe8d41340436a90ef8b1219a3cbb127", size = 223219, upload-time = "2026-06-30T07:15:12.476Z" },
{ url = "https://files.pythonhosted.org/packages/d7/a2/145afacf796e4506062825941176ad9445c2dcf2b3b6a1f13d3030a15e19/rpds_py-2026.6.3-cp311-cp311-win_arm64.whl", hash = "sha256:168c733a7112e071bb7a66460e667edfcff06c017a3c523f7a8a8e08d0140804", size = 219137, upload-time = "2026-06-30T07:15:13.631Z" },
{ url = "https://files.pythonhosted.org/packages/5c/be/2e8974163072e7bab7df1a5acd54c4498e75e35d6d18b864d3a9d5dadc92/rpds_py-2026.6.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a0811d33247c3d6128a3001d763f2aa056bb3425204335400ac54f89eec3a0d0", size = 343691, upload-time = "2026-06-30T07:15:14.96Z" },
{ url = "https://files.pythonhosted.org/packages/a4/73/319dfa745dd668efe89309141ded489126461fcecd2b8f3a3cda185129b6/rpds_py-2026.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:538949e262e46caa31ac01bdb3c1e8f642622922cacbabbae6a8445d9dc33eaf", size = 338542, upload-time = "2026-06-30T07:15:16.267Z" },
{ url = "https://files.pythonhosted.org/packages/21/63/4239893be1c4d09b709b1a8f6be4188f0870084ff547f46606b8a75f1b03/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55927d532399c2c646100ff7feb48eaa940ad70f42cd68e1328f3ded9f81ca24", size = 368180, upload-time = "2026-06-30T07:15:17.62Z" },
{ url = "https://files.pythonhosted.org/packages/1c/ca/9c5de382225234ceb37b1844ebdb140db12b2a278bb9efe2fcd19f6c82ce/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f56f1695bc5c0871cbc33dc0130fcf503aab0c57dcc5a6700a4f49eba4f2652e", size = 375067, upload-time = "2026-06-30T07:15:18.952Z" },
{ url = "https://files.pythonhosted.org/packages/87/dc/863f69d1bf04ade34b7fe0d59b9fdf6f0135fe2d7cbca74f1d665589559d/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:270b293dae9058fc9fcedab50f13cebf46fb8ed1d1d54e0521a9da5d6b211975", size = 490509, upload-time = "2026-06-30T07:15:20.434Z" },
{ url = "https://files.pythonhosted.org/packages/ce/ef/eac16a12048b45ec7c7fa94f2be3438a5f26bf9cc8580b18a1cfd609b7f6/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:127565fead0a10943b282957bd5447804ff3160ad79f2ad2635e6d249e380680", size = 382754, upload-time = "2026-06-30T07:15:21.831Z" },
{ url = "https://files.pythonhosted.org/packages/04/8f/d2f3f532616be4d06c316ef119683e832bd3d41e112bf3a88f4151c95b17/rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecabd69db66de867690f9797f2f8fa27ba501bbc24540cbdbdc649cd15888ba6", size = 366189, upload-time = "2026-06-30T07:15:23.371Z" },
{ url = "https://files.pythonhosted.org/packages/e3/29/41a7b0e98a4b44cd676ab7598419623373eb43b20be68c084935c1a8cf88/rpds_py-2026.6.3-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:58eadac9cd119677b60e1cf8ac4052f35949d71b8a9e5556efccbe82533cf22a", size = 377750, upload-time = "2026-06-30T07:15:24.659Z" },
{ url = "https://files.pythonhosted.org/packages/2e/05/ecda0bec46f9a1565090bcdc941d023f6a25aff85fda28f89f8d19878152/rpds_py-2026.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7491ee23305ac3eb59e492b6945881f5cd77a6f731061a3f25b77fd40f9e99a4", size = 395576, upload-time = "2026-06-30T07:15:25.987Z" },
{ url = "https://files.pythonhosted.org/packages/68/a8/6ed52f03ee6cb854ce78785cc9a9a672eb880e83fd7224d471f667d151f1/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2c99f7e8ccb3dd6e3e4bfeac657a7b208c9bac8075f4b078c02d7404c34107fa", size = 543807, upload-time = "2026-06-30T07:15:27.356Z" },
{ url = "https://files.pythonhosted.org/packages/8f/d6/156c0d3eea27ba09b92562ba2364ba124c0a061b199e17eac637cd25a5e2/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:62698275682bf121181861295c9181e789030a2d516071f5b8f3c23c170cd0fc", size = 611187, upload-time = "2026-06-30T07:15:28.931Z" },
{ url = "https://files.pythonhosted.org/packages/f1/31/774212ed989c62f7f310220089f9b0a3fb8f40f5443d1727abd5d9f52bc9/rpds_py-2026.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a214c993455f99a89aaeadc9b21241900037adc9d97203e374d75513c5911822", size = 573030, upload-time = "2026-06-30T07:15:30.553Z" },
{ url = "https://files.pythonhosted.org/packages/c9/50/22f73127a41f1ce4f87fe39aadfb9a126345801c274aa93ae88456249327/rpds_py-2026.6.3-cp312-cp312-win32.whl", hash = "sha256:501f9f04a588d6a09179368c57071301445191767c64e4b52a6aa9871f1ef5ed", size = 202185, upload-time = "2026-06-30T07:15:32.027Z" },
{ url = "https://files.pythonhosted.org/packages/04/3a/f0ee4d4dde9d3b69dedf1b5f74e7a40017046d55052d173e418c6a94f960/rpds_py-2026.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:2c958bf94822e9290a40aaf2a822d4bc5c88099093e3948ad6c571eca9272e5f", size = 220394, upload-time = "2026-06-30T07:15:33.359Z" },
{ url = "https://files.pythonhosted.org/packages/f3/83/3382fe37f809b59f02aac04dbc4e765b480b46ee0227ed516e3bdc4d3dfc/rpds_py-2026.6.3-cp312-cp312-win_arm64.whl", hash = "sha256:22bffe6042b9bcb0822bcd1955ec00e245daf17b4344e4ed8e9551b976b63e96", size = 215753, upload-time = "2026-06-30T07:15:34.778Z" },
{ url = "https://files.pythonhosted.org/packages/a4/9e/b818ee580026ec578138e961027a68820c40afeb1ec8f6819b54fb99e196/rpds_py-2026.6.3-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:3cfe765c1da0072636ca06628261e0ea05688e160d5c8a03e0217c3854037223", size = 343012, upload-time = "2026-06-30T07:15:36.005Z" },
{ url = "https://files.pythonhosted.org/packages/f3/6b/686d9dc4359a8f163cfbbf89ee0b4e586431de22fe8248edb63a8cf50d49/rpds_py-2026.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f4d78253f6996be4901669ad25319f842f740eccf4d58e3c7f3dd39e6dde1d8f", size = 338203, upload-time = "2026-06-30T07:15:37.462Z" },
{ url = "https://files.pythonhosted.org/packages/9e/9b/069aa329940f8207615e091f5eedbbd40e1e15eac68a0790fd05ccdf796c/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54f45a148e28767bf343d33a684693c70e451c6f4c0e9904709a723fafbdfc1f", size = 367984, upload-time = "2026-06-30T07:15:39.008Z" },
{ url = "https://files.pythonhosted.org/packages/14/db/34c203e4becff3703e4d3bc121842c00b8689197f398161203a880052f4e/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:842e7b070435622248c7a2c44ae53fa1440e073cc3023bc919fed570884097a7", size = 374815, upload-time = "2026-06-30T07:15:40.253Z" },
{ url = "https://files.pythonhosted.org/packages/ee/7d/8071067d2cc453d916ad836e828c943f575e8a44612537759002a1e07381/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8020133a74bd81b4572dd8e4be028a6b1ebcd70e6726edc3918008c08bee6ee6", size = 490545, upload-time = "2026-06-30T07:15:41.729Z" },
{ url = "https://files.pythonhosted.org/packages/a3/42/da06c5aa8f0484ff07f270787434204d9f4535e2f8c3b51ed402267e63c3/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cdc7e35386f3847df728fbcb5e887e2d79c19e2fa1eba9e51b6621d23e3243af", size = 382828, upload-time = "2026-06-30T07:15:43.327Z" },
{ url = "https://files.pythonhosted.org/packages/57/d7/fe978efc2ae50abe48eb7464668ea99f53c010c60aeebb7b35ad27f23661/rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acac386b453c2516111b50985d60ce46e7fadb5ea71ae7b25f4c946935bf27cf", size = 365678, upload-time = "2026-06-30T07:15:44.992Z" },
{ url = "https://files.pythonhosted.org/packages/69/9d/1d8922e1990b2a6eb532b6ff53d3e73d2b3bbffc84116c75826bee73dfc6/rpds_py-2026.6.3-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:425560c6fa0415f27261727bb20bd097568485e5eb0c121f1949417d1c516885", size = 377811, upload-time = "2026-06-30T07:15:46.523Z" },
{ url = "https://files.pythonhosted.org/packages/b1/3d/198dceafb4fb034a6a47347e1b0735d34e0bd4a50be4e898d408ee66cb14/rpds_py-2026.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a550fb4950a06dde3beb4721f5ad4b25bf4513784665b0a8522c792e2bd822a4", size = 395382, upload-time = "2026-06-30T07:15:47.955Z" },
{ url = "https://files.pythonhosted.org/packages/1f/f1/13968e49655d40b6b19d8b9140296bbc6f1d86b3f0f6c346cf9f1adddf4b/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f4bca01b63096f606e095734dd56e74e175f94cfbf24ff3d63281cec61f7bb7", size = 543832, upload-time = "2026-06-30T07:15:49.33Z" },
{ url = "https://files.pythonhosted.org/packages/ac/ab/289bcb1b90bd3e40a2900c561fa0e2087345ecbb094f0b870f2345142b7c/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ccffae9a092a00deb7efd545fe5e2c33c33b88e7c054337e9a74c179347d0b7d", size = 611011, upload-time = "2026-06-30T07:15:50.847Z" },
{ url = "https://files.pythonhosted.org/packages/1e/16/5043105e679436ccfbc8e5e0dd2d663ed18a8b8113515fd06a5e5d77c83e/rpds_py-2026.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1cf01971c4f2c5553b772a542e4aaf191789cd331bc2cd4ff0e6e65ba49e1e97", size = 572431, upload-time = "2026-06-30T07:15:52.394Z" },
{ url = "https://files.pythonhosted.org/packages/85/ed/adab103321c0a6565d5ae1c2998349bc3ee175b82ccc5ae8fc04cc413075/rpds_py-2026.6.3-cp313-cp313-win32.whl", hash = "sha256:8c3d1e9c15b9d51ca0391e13da1a25a0a4df3c58a37c9dc368e0736cf7f69df0", size = 201710, upload-time = "2026-06-30T07:15:53.894Z" },
{ url = "https://files.pythonhosted.org/packages/7b/ed/a03b09668e74e5dabbf2e211f6468e1820c0552f7b0500082da31841bf7b/rpds_py-2026.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:9250a9a0a6fd4648b3f868da8d91a4c52b5811a62df58e753d50ae4454a36f80", size = 219454, upload-time = "2026-06-30T07:15:55.25Z" },
{ url = "https://files.pythonhosted.org/packages/27/17/b8642c12930b71bc2b25831f6708ccf0f75abcd11883932ec9ce54ba3a78/rpds_py-2026.6.3-cp313-cp313-win_arm64.whl", hash = "sha256:900a67df3fd1660b035a4761c4ce73c382ea6b35f90f9863c36c6fd8bf8b09bb", size = 215063, upload-time = "2026-06-30T07:15:56.573Z" },
{ url = "https://files.pythonhosted.org/packages/b6/36/7fbe9dcdaf857fb3f63c2a2284b62492d95f5e8334e947e5fb6e7f68c9be/rpds_py-2026.6.3-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:931908d9fc855d8f74783377822be318edb6dcb19e47169dc038f9a1bf60b06e", size = 344510, upload-time = "2026-06-30T07:15:57.921Z" },
{ url = "https://files.pythonhosted.org/packages/ba/54/f785cc3d3f60839ca57a5af4927a9f347b07b2799c373fc20f7949f87c7e/rpds_py-2026.6.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d7469697dce35be237db177d42e2a2ee26e6dcc5fc052078a6fefabd288c6edd", size = 339495, upload-time = "2026-06-30T07:15:59.238Z" },
{ url = "https://files.pythonhosted.org/packages/63/ef/d4cdaf309e6b095b43597103cf8c0b951d6cca2acce68c474f75ec12e0c7/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcfbcf66006befb9fd2aeaa9e01feaf881b4dc330a02ba07d2322b1c11be7b5d", size = 369454, upload-time = "2026-06-30T07:16:01.021Z" },
{ url = "https://files.pythonhosted.org/packages/96/4a/9559a68b7ee15db09d7981212e8c2e219d2a1d6d4faa0391d813c3496a36/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847927daf4cffbd4e90e42bc890069897101edd015f956cb8721b3473372edda", size = 374583, upload-time = "2026-06-30T07:16:02.287Z" },
{ url = "https://files.pythonhosted.org/packages/ef/75/8964aa7d2c6e8ac43eba8eb6e6b0fdda1f46d39f2fc3e6aa9f2cb17f485d/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aca6c1ef08a82bfe327cc156da694660f599923e2e6665b6d81c9c2d0ac9ffc8", size = 492919, upload-time = "2026-06-30T07:16:03.723Z" },
{ url = "https://files.pythonhosted.org/packages/8f/97/6908094ac804115e65aedfd90f1b5fee4eebebd3f6c4cfc5419939267565/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ae50181a047c871561212bb97f7932a2d45fb53e947bd9b57ebad85b529cbc53", size = 383725, upload-time = "2026-06-30T07:16:05.305Z" },
{ url = "https://files.pythonhosted.org/packages/d1/9c/0d1fdc2e7aba23e290d603bc494e97bd205bae262ce33c6b32a69768ed5e/rpds_py-2026.6.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc319e5a1de4b6913aac94bf6a2f9e847371e0a140a43dd4991db1a09bc2d504", size = 367255, upload-time = "2026-06-30T07:16:07.086Z" },
{ url = "https://files.pythonhosted.org/packages/c4/fe/f0209ca4a9ed074bc8acb44dfd0e81c3122e94c9689f5645b7973a866719/rpds_py-2026.6.3-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:e4316bf32babbed84e691e352faf967ce2f0f024174a8643c37c94a1080374fc", size = 379060, upload-time = "2026-06-30T07:16:08.525Z" },
{ url = "https://files.pythonhosted.org/packages/c6/8d/f1cc54c616b9d8897de8738aac148d20afca93f68187475fe194d09a71b9/rpds_py-2026.6.3-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8c6e5a2f750cc71c3e3b11d71661f21d6f9bc6cebc6564b1466417a1ec03ec77", size = 395960, upload-time = "2026-06-30T07:16:09.989Z" },
{ url = "https://files.pythonhosted.org/packages/fb/04/aafff00f73aeca2945f734f1d483c64ab8f472d0864ab02377fd8e89c3b2/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4470ce197d4090875cf6affbf1f853338387428df97c4fb7b7106317b8214698", size = 545356, upload-time = "2026-06-30T07:16:11.816Z" },
{ url = "https://files.pythonhosted.org/packages/fd/cc/e229663b9e4ddac5a4acbe9085dd80a71af2a5d356b8b39d6bff233f24b0/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:ea964164cc9afa72d4d9b23cc28dafae93693c0a53e0b42acbff15b22c3f9ddd", size = 612319, upload-time = "2026-06-30T07:16:13.586Z" },
{ url = "https://files.pythonhosted.org/packages/e3/7a/8a0e6d3e6cd066af108b71b43122c3fe158dd9eb86acac626593a2582eb1/rpds_py-2026.6.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:639c8929aa0afe81be836b04de888460d6bed38b9c54cfc18da8f6bfabf5af5d", size = 573508, upload-time = "2026-06-30T07:16:15.23Z" },
{ url = "https://files.pythonhosted.org/packages/87/03/2a69ab618a789cf6cf85c86bb844c62d090e700ab1a2aa676b3741b6c516/rpds_py-2026.6.3-cp314-cp314-win32.whl", hash = "sha256:882076c00c0a608b131187055ddc5ae29f2e7eaf870d6168980420d58528a5c8", size = 202504, upload-time = "2026-06-30T07:16:16.893Z" },
{ url = "https://files.pythonhosted.org/packages/85/62/a3892ba945f4e24c78f352e5de3c7620d8479f73f211406a97263d13c7d2/rpds_py-2026.6.3-cp314-cp314-win_amd64.whl", hash = "sha256:0be972be84cfcaf46c8c6edf690ca0f154ac17babf1f6a955a51579b34ad2dc5", size = 220380, upload-time = "2026-06-30T07:16:18.108Z" },
{ url = "https://files.pythonhosted.org/packages/3d/e7/c2bd44dc831931815ad11ebb5f430b5a0a4d3caa9de837107876c30c3432/rpds_py-2026.6.3-cp314-cp314-win_arm64.whl", hash = "sha256:2a9c6f195058cb45335e8cc3802745c603d716eb96bc9625950c1aac71c0c703", size = 215976, upload-time = "2026-06-30T07:16:19.654Z" },
{ url = "https://files.pythonhosted.org/packages/79/9c/fff7b74bce9a091ec9a012a03f9ff5f69364eaf9451060dfc4486da2ffdd/rpds_py-2026.6.3-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:f90938e92afda60266da758ee7d363447f7f0138c9559f9e1811629580582d90", size = 346840, upload-time = "2026-06-30T07:16:21.268Z" },
{ url = "https://files.pythonhosted.org/packages/e9/44/77bcb1168b33704908295533d27f10eb811e9e3e193e8993dc99572211d3/rpds_py-2026.6.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ec829541c45bca16e61c7ae50c20501f213605beb75d1aba91a6ee37fbbb56a4", size = 340282, upload-time = "2026-06-30T07:16:22.875Z" },
{ url = "https://files.pythonhosted.org/packages/87/3c/7a9081c7c9e645b39efe19e4ffbeccd80add246327cd9b888aecffd72317/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afd70d95892096cdb26f15a00c45907b17817577aa8d1c76b2dcc2788391f9e9", size = 370403, upload-time = "2026-06-30T07:16:24.415Z" },
{ url = "https://files.pythonhosted.org/packages/f7/69/af47021eb7dad6ff3396cb001c08f0f3c4d06c20253f75be6421a59fe6b7/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:29dfa0533a5d4c94d4dfa1b694fcb56c9c63aad8330ffdd816fd225d0a7a162f", size = 376055, upload-time = "2026-06-30T07:16:26.111Z" },
{ url = "https://files.pythonhosted.org/packages/81/fc/a3bcf517084396a6dd258c592567a3c011ba4557f2fde23dceaf26e74f2e/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:af05d726809bff6b141be124d4c7ce998f9c9c7f30edb1f46c07aa103d540b41", size = 494419, upload-time = "2026-06-30T07:16:27.596Z" },
{ url = "https://files.pythonhosted.org/packages/c9/eb/13d529d1788135425c7bf207f8463458ca5d92e43f3f701365b83e9dffc1/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9826217f048f620d9a712672818bf231442c1b35d96b227a07eabd11b4bb6945", size = 384848, upload-time = "2026-06-30T07:16:29.183Z" },
{ url = "https://files.pythonhosted.org/packages/8e/f4/b7ac49f30013aba8f7b9566b1dd07e81de95e708c1374b7bacc5b9bc5c9c/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:536bceea4fa4acf7e1c61da2b5786304367c816c8895be71b8f537c480b0ea1f", size = 371369, upload-time = "2026-06-30T07:16:30.912Z" },
{ url = "https://files.pythonhosted.org/packages/31/86/6260bafa622f788b07ddec0e52d810305c8b9b0b8c27f58a2ab04bf62b4f/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:bc0011654b91cc4fb2ae701bec0a0ba1e552c0714247fa7af6c59e0ccfa3a4e1", size = 379673, upload-time = "2026-06-30T07:16:32.486Z" },
{ url = "https://files.pythonhosted.org/packages/19/c3/03f1ee79a047b48daeca157c89a18509cde22b6b951d642b9b0af1be660a/rpds_py-2026.6.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:539d75de9e0d536c84ff18dfeb805398e58227001ce09231a26a08b9aed1ee0e", size = 397500, upload-time = "2026-06-30T07:16:34.471Z" },
{ url = "https://files.pythonhosted.org/packages/f0/95/8ed0cd8c377dca12aea498f119fe639fc474d1461545c39d2b5872eb1c0f/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:166cf54d9f44fc6ceb53c7860258dde44a81406646de79f8ed3234fca3b6e538", size = 545978, upload-time = "2026-06-30T07:16:36.45Z" },
{ url = "https://files.pythonhosted.org/packages/d3/f2/0eb57f0eaa83f8fc152a7e03de968ab77e1f00732bebc892b190c6eebde7/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:d34c20167764fbcf927194d532dd7e0c56772f0a5f943fa5ef9e9afbba8fb9db", size = 613350, upload-time = "2026-06-30T07:16:38.213Z" },
{ url = "https://files.pythonhosted.org/packages/5b/de/e0674bdbc3ef7634989b3f854c3f34bc1f587d36e5bfdc5c378d57034619/rpds_py-2026.6.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ea7bb13b7c9a29791f87a0387ba7d3ad3a6d783d827e4d3f27b40a0ff44495e2", size = 576486, upload-time = "2026-06-30T07:16:39.797Z" },
{ url = "https://files.pythonhosted.org/packages/f2/f6/21101359743cd136ada781e8210a85769578422ba460672eea0e29739200/rpds_py-2026.6.3-cp314-cp314t-win32.whl", hash = "sha256:6de4744d05bd1aa1be4ed7ea1189e3979196808008113bbbf899a460966b925e", size = 201068, upload-time = "2026-06-30T07:16:41.316Z" },
{ url = "https://files.pythonhosted.org/packages/a6/b2/9574d4d44f7760c2aa32d92a0a4f41698e33f5b204a0bf5c9758f52c79d5/rpds_py-2026.6.3-cp314-cp314t-win_amd64.whl", hash = "sha256:c7b9a2f8f4d8e90af72571d3d495deebdd7e3c75451f5b41719aee166e940fc2", size = 220600, upload-time = "2026-06-30T07:16:43.091Z" },
{ url = "https://files.pythonhosted.org/packages/08/ae/f23a2697e6ee6340a578b0f136be6483657bef0c6f9497b752bb5c0964bb/rpds_py-2026.6.3-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:e059c5dde6452b44424bd1834557556c226b57781dee1227af23518459722b13", size = 344726, upload-time = "2026-06-30T07:16:44.5Z" },
{ url = "https://files.pythonhosted.org/packages/c3/63/e7b3a1a5358dd32c930a1062d8e15b67fd6e8922e81df9e91706d66ee5c8/rpds_py-2026.6.3-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2f7c26fbc5acd2522b95d4177fe4710ffd8e9b20529e703ffbf8db4d93903f05", size = 339587, upload-time = "2026-06-30T07:16:46.255Z" },
{ url = "https://files.pythonhosted.org/packages/ec/64/10a85681916ca55fffb91b0a211f84e34297c109243484dd6394660a8a7c/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3086b538543802f84c843911242db20447de00d8752dd0efc936dbcf02218ba", size = 369585, upload-time = "2026-06-30T07:16:48.101Z" },
{ url = "https://files.pythonhosted.org/packages/76/c2/baf95c7c38823e12ba34407c5f5767a89e5cf2233895e56f608167ae9493/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f2e5c5ee828d42cb11760761c0af6507927bec42d0ad5458f97c9203b054617", size = 375479, upload-time = "2026-06-30T07:16:49.93Z" },
{ url = "https://files.pythonhosted.org/packages/6a/94/0aad06c72d65101e11d33528d438cda99a39ce0da99466e156158f2541d3/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed0c1e5d10cdc7135537988c74a0188da68e2f3c30813ba3744ab1e42e0480f9", size = 492418, upload-time = "2026-06-30T07:16:51.641Z" },
{ url = "https://files.pythonhosted.org/packages/b5/17/de3f5a479a1f056535d7489819639d8cd591ea6281d700390b43b1abd745/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c2642a7603ec0b16ed77da4555db3b4b472341904873788327c0b0d7b95f1bb", size = 384123, upload-time = "2026-06-30T07:16:53.622Z" },
{ url = "https://files.pythonhosted.org/packages/46/7d/bf09bd1b145bb2671c03e1e6d1ab8651858d90d8c7dfeadd85a37a934fd8/rpds_py-2026.6.3-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e4320744c1ffdd95a603def63344bfab2d33edeab301c5007e7de9f9f5b3885", size = 367351, upload-time = "2026-06-30T07:16:55.241Z" },
{ url = "https://files.pythonhosted.org/packages/a3/ea/1bb734f314b8be319149ddee80b18bd41372bdcfbdf88d28131c0cd37719/rpds_py-2026.6.3-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:a9f4645593036b81bbdb36b9c8e0ea0d1c3fee968c4d59db0344c14087ef143a", size = 378827, upload-time = "2026-06-30T07:16:56.841Z" },
{ url = "https://files.pythonhosted.org/packages/4b/93/d9611e5b25e26df9a3649813ed66193ace9347a7c7fc4ab7cf70e94851c0/rpds_py-2026.6.3-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e55d236be29255554da47abe5c577637db7c24a02b8b46f0ca9524c855801868", size = 395966, upload-time = "2026-06-30T07:16:58.557Z" },
{ url = "https://files.pythonhosted.org/packages/c3/cb/99d77e16e5534ae1d90629bbe419ba6ee170833a6a85e3aa1cc41726fbbc/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:24e9c5386e16669b674a69c156c8eeefcb578f3b3397b713b08e6d60f3c7b187", size = 545680, upload-time = "2026-06-30T07:17:00.164Z" },
{ url = "https://files.pythonhosted.org/packages/59/15/11a29755f790cef7a2f755e8e14f4f0c33f39489e1893a632a2eee59672b/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:c60924535c75f1566b6eb75b5c31a48a43fef04fa2d0d201acbad8a9969c6107", size = 611853, upload-time = "2026-06-30T07:17:01.962Z" },
{ url = "https://files.pythonhosted.org/packages/68/86/0c27547e21644da938fb530f7e1a8148dd24d02db07e7a5f2567a17ce710/rpds_py-2026.6.3-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:38a2fea2787428f811719ceb9114cb78964a3138838320c29ac39526c79c16ba", size = 573715, upload-time = "2026-06-30T07:17:03.693Z" },
{ url = "https://files.pythonhosted.org/packages/29/71/4d8fcf700931815594bce892255bbd973b94efaf0fc1932b0590df18d886/rpds_py-2026.6.3-cp315-cp315-win32.whl", hash = "sha256:d483fe17f01ad64b7bf7cc38fcefff1ca9fb83f8c2b2542b68f97ffe0611b369", size = 202864, upload-time = "2026-06-30T07:17:05.746Z" },
{ url = "https://files.pythonhosted.org/packages/eb/62/b577562de0edbb55b2be85ce5fd09c33e386b9b13eee09833af4240fd5c4/rpds_py-2026.6.3-cp315-cp315-win_amd64.whl", hash = "sha256:67e3a721ffc5d8d2210d3671872298c4a84e4b8035cfe42ffd7cde35d772b146", size = 220430, upload-time = "2026-06-30T07:17:07.471Z" },
{ url = "https://files.pythonhosted.org/packages/c8/95/d6d0b2509825141eef60669a5739eec88dbc6a48053d6c92993a5704defe/rpds_py-2026.6.3-cp315-cp315-win_arm64.whl", hash = "sha256:6e84adbcf4bf841aed8116a8264b9f50b4cb3e7bd89b516122e616ac56ca269e", size = 215877, upload-time = "2026-06-30T07:17:09.008Z" },
{ url = "https://files.pythonhosted.org/packages/b7/bf/f3ea278f0afd615c1d0f19cb69043a41526e2bb600c2b536eb192218eb27/rpds_py-2026.6.3-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:ae6dd8f10bd17aad820876d24caec9efdafd80a318d16c0a48edb5e136902c6b", size = 346933, upload-time = "2026-06-30T07:17:10.762Z" },
{ url = "https://files.pythonhosted.org/packages/9d/29/9907bdf1c5346763cf10b7f6852aad86652168c259def904cbe0082c5864/rpds_py-2026.6.3-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:bdbd97738551fca3917c1bd7188bec1920bb520104f28e7e1007f9ceb17b7690", size = 340274, upload-time = "2026-06-30T07:17:12.266Z" },
{ url = "https://files.pythonhosted.org/packages/6f/2c/8e03767b5778ef25cebf74a7a91a2c3806f8eced4c92cb7406bbe060756d/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b95977e7211527ab0ba576e286d023389fbeeb32a6b7b771665d333c60e5342", size = 370763, upload-time = "2026-06-30T07:17:14.107Z" },
{ url = "https://files.pythonhosted.org/packages/2e/e1/df2a7e1ba2efd796af26194250b8d42c821b46592311595162af9ef0528d/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15fde0e6fb0d88a60d221204873743e5d9f0b7d29165e62cd86d0413ad74ba6", size = 376467, upload-time = "2026-06-30T07:17:15.76Z" },
{ url = "https://files.pythonhosted.org/packages/6b/de/8a0814d1946af29cb068fb259aa8622f856df1d0bab58429448726b537f5/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a136d453475ac0fcbda502ef1e6504bd28d6d904700915d278deeab0d00fe140", size = 496689, upload-time = "2026-06-30T07:17:17.308Z" },
{ url = "https://files.pythonhosted.org/packages/df/f3/f19e0c852ba13694f5a79f3b719331051573cb5693feacf8a88ffffc3a71/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f826877d462181e5eb1c26a0026b8d0cab05d99844ecb6d8bf3627a2ca0c0442", size = 385340, upload-time = "2026-06-30T07:17:18.928Z" },
{ url = "https://files.pythonhosted.org/packages/e2/ae/7ec3a9d2d4351f99e37bcb06b6b6f954512646bfdbf9742e1de727865daf/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79486287de1730dbaff3dbd124d0ca4d2ef7f9d29bf2544f1f93c09b5bcbbd12", size = 372179, upload-time = "2026-06-30T07:17:20.539Z" },
{ url = "https://files.pythonhosted.org/packages/d3/ac/9cee911dff2aaa9a5a8354f6610bf2e6a616de9197c5fff4f54f82585f1e/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:808345f53cb952433ca2816f1604ff3515608a81784954f38d4452acfe8e61d5", size = 379993, upload-time = "2026-06-30T07:17:22.212Z" },
{ url = "https://files.pythonhosted.org/packages/83/6b/7c2a07ba88d1e9a936612f7a5d067467ed03d971d5a06f7d309dff044a7e/rpds_py-2026.6.3-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1967debc37f64f2c4dc90a7f563aec558b471966e12adcac4e1c4240496b6ebf", size = 398909, upload-time = "2026-06-30T07:17:23.66Z" },
{ url = "https://files.pythonhosted.org/packages/97/0b/776ffcb66783637b0031f6d58d6fb55913c8b5abf00aeecd46bf933fb477/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:f0840b5b17057f7fd918b76183a4b5a0635f43e14eb2ce60dce1d4ee4707ea00", size = 546584, upload-time = "2026-06-30T07:17:25.264Z" },
{ url = "https://files.pythonhosted.org/packages/55/33/ba3bc04d7092bd553c9b2b195624992d2cc4f3de1f380b7b93cbee67bd79/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:faa679d19a6696fd54259ad321251ad77a13e70e03dd834daa762a44fb6196ef", size = 614357, upload-time = "2026-06-30T07:17:26.888Z" },
{ url = "https://files.pythonhosted.org/packages/8b/71/14edf065f04630b1a8472f7653cad03f6c478bcf95ea0e6aed55451e33ea/rpds_py-2026.6.3-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:23a439f31ccbeff1574e24889128821d1f7917470e830cf6544dced1c662262a", size = 576533, upload-time = "2026-06-30T07:17:28.546Z" },
{ url = "https://files.pythonhosted.org/packages/ba/76/65002b08596c389105720a8c0d22298b8dc25a4baf89b2ce431343c8b1de/rpds_py-2026.6.3-cp315-cp315t-win32.whl", hash = "sha256:913ca42ccad3f8cc6e292b587ae8ae49c8c823e5dce51a736252fc7c7cdfa577", size = 201204, upload-time = "2026-06-30T07:17:30.193Z" },
{ url = "https://files.pythonhosted.org/packages/8c/97/d855d6b3c322d1f27e26f5241c42016b56cf01377ea8ed348285f54652f0/rpds_py-2026.6.3-cp315-cp315t-win_amd64.whl", hash = "sha256:ae3d4fe8c0b9213624fdce7279d70e3b148b682ca20719ebd193a23ebfa47324", size = 220719, upload-time = "2026-06-30T07:17:31.788Z" },
{ url = "https://files.pythonhosted.org/packages/b4/9c/f0d19ac587fd0e4ab6b72cda355e9c5a6166b01ef7e064e437aef8eb9fef/rpds_py-2026.6.3-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:4cf2d36a2357e4d07bb5a4f98801265327b48256867816cfd2ceb001e9754a8f", size = 349791, upload-time = "2026-06-30T07:17:33.315Z" },
{ url = "https://files.pythonhosted.org/packages/38/c7/1d49d204c9fd2ee6c537601dc4c1ba921e03363ca576bfab94a00254ac9a/rpds_py-2026.6.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:30c6dc199b24a5e3e81d50da0f00858c5bbdb2617a750395687f4339c5818171", size = 352842, upload-time = "2026-06-30T07:17:34.897Z" },
{ url = "https://files.pythonhosted.org/packages/ac/e5/c0b5dc93cd0d4c06ce1f438907649514e2ea077bcd911e3154a51e96c38e/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9891e594296ab9dada6551c8e7b387b2721f27a67eecd528412e8906247a7b90", size = 382094, upload-time = "2026-06-30T07:17:36.514Z" },
{ url = "https://files.pythonhosted.org/packages/0d/54/ec0e907b4ca8d541112db352409bd15f871c9b243e0c92c9b5a46ae96f01/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b5c2dc92304aa48a4a60443b548bb12f12e119d4b72f314015e67b9e1be97fca", size = 388662, upload-time = "2026-06-30T07:17:38.235Z" },
{ url = "https://files.pythonhosted.org/packages/d3/f4/921c22a4fd0f1c1ac13a3996ffbf0aa67951e2c8ad0d1d9574938a2932e8/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:127e08c0642d880cf32ca47ec2a4a77b901f7e2dd1ad9762adb13955d72ffcc9", size = 504896, upload-time = "2026-06-30T07:17:39.689Z" },
{ url = "https://files.pythonhosted.org/packages/0b/1b/a114b972cefa1ab1cdb3c7bb177cd3844a12826c507c722d3a73516dbbaf/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8bb68f03f395eb793220b45c097bd4d8c32944393da0fad8b999efac0868fc8c", size = 391545, upload-time = "2026-06-30T07:17:41.336Z" },
{ url = "https://files.pythonhosted.org/packages/4e/98/af9b3db77d47fcbe6c8c1f36e2c2147ec70292819e99c325f871584a1c11/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3450b693fde92133e9f51060568a4c31fcca76d5e53bbd611e689ca446517e9", size = 380059, upload-time = "2026-06-30T07:17:42.857Z" },
{ url = "https://files.pythonhosted.org/packages/c9/ba/0efd8668b97c1d26a61566386c636a7a7a09829e474fdf807caa15a2c844/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:5e8d07bddee435a2ff6f1920e18feff28d0bc4533e42f4bf6927fbd073312c41", size = 393235, upload-time = "2026-06-30T07:17:44.637Z" },
{ url = "https://files.pythonhosted.org/packages/62/90/8c139ee9690f73b0829f32647de6f40d826f8f443af6fa72644f96351aac/rpds_py-2026.6.3-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3a83ae6c67b7676b9878378547ca8e93ed77a580037bcbcd1d32f739e1e6089c", size = 413008, upload-time = "2026-06-30T07:17:46.225Z" },
{ url = "https://files.pythonhosted.org/packages/9c/97/0043896fdd7828ce09a1d9a8b06433714d0960fc4ff3fc4aa72b666b764e/rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:2bfd04c19ddbd6640de0b51894d764bd2758854d5b75bd102d2ef10cb9c293a9", size = 558118, upload-time = "2026-06-30T07:17:47.759Z" },
{ url = "https://files.pythonhosted.org/packages/f6/40/02355f0e134f783a8f9814c4680a1bd311d37671577a5964ea838573ff37/rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:ca6546b66be9dc4738b1b043d5ebd5488c66c578c5ff0fd0e8065313fe3afb76", size = 623138, upload-time = "2026-06-30T07:17:49.355Z" },
{ url = "https://files.pythonhosted.org/packages/10/85/48f0abdcef5cce4e034c7a5b0ceeceba0b01bf0d942824f4bb720afe2dec/rpds_py-2026.6.3-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:8e65860d238379ed982fd9ba690579b5e95af2f4840f99c772816dbe573cb826", size = 586486, upload-time = "2026-06-30T07:17:51.141Z" },
]
[[package]]
name = "sentry-sdk"
version = "2.70.0"
@@ -2298,6 +2735,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/c5/7a/7d1879d78c086737acee5f3250cbff9d285e5bd7cb6bc2c730f24e8d400e/sqlalchemy-2.1.1-py3-none-any.whl", hash = "sha256:4357c1a222e141662251a59d3702f9b124294941171dfa6bd570513e9f4905ee", size = 2047471, upload-time = "2026-09-25T15:20:37.797Z" },
]
[[package]]
name = "sse-starlette"
version = "3.5.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "starlette" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e4/be/0123026f719d1a7936f214a88b553bb5701e04ff2511147c1dab0c5035eb/sse_starlette-3.5.0.tar.gz", hash = "sha256:75de713aa8a9441513cc283220826da079d982770965b951e9437720e8bafdb2", size = 36794, upload-time = "2026-09-28T17:48:14.7Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/be/e4/cdda14023c316d71493bc54fdffc3dd006631b88866145c9d3cc33e0f1df/sse_starlette-3.5.0-py3-none-any.whl", hash = "sha256:3e6e1070df3f0f5d9cea81496de92dbb72f6721871d99748ece67441dd8b7997", size = 17407, upload-time = "2026-09-28T17:48:13.228Z" },
]
[[package]]
name = "starlette"
version = "1.7.0"