Merge remote-tracking branch 'origin/master' into dev/4.11.x

# Conflicts:
#	src/langbot/pkg/pipeline/preproc/preproc.py
#	src/langbot/pkg/pipeline/process/handlers/chat.py
#	src/langbot/pkg/provider/runners/localagent.py
#	src/langbot/pkg/provider/tools/toolmgr.py
#	src/langbot/templates/metadata/pipeline/ai.yaml
#	tests/unit_tests/test_preproc.py
#	web/src/app/home/components/dynamic-form/DynamicFormComponent.tsx
#	web/src/app/home/pipelines/components/pipeline-form/PipelineFormComponent.tsx
This commit is contained in:
Junyan Qin
2026-06-30 21:07:13 +08:00
167 changed files with 16186 additions and 1722 deletions
+65 -22
View File
@@ -24,7 +24,10 @@ await ensureEvidence(paths);
const writeEnv = process.argv.includes("--write-env");
const frontendUrl = env.LANGBOT_FRONTEND_URL || "";
const backendUrl = env.LANGBOT_BACKEND_URL || "";
const pipelineName = env.LANGBOT_E2E_CREATE_PIPELINE_NAME || env.LANGBOT_QA_AGENT_RUNNER_PIPELINE_NAME || DEFAULT_PIPELINE_NAME;
const pipelineName =
env.LANGBOT_E2E_CREATE_PIPELINE_NAME ||
env.LANGBOT_QA_AGENT_RUNNER_PIPELINE_NAME ||
DEFAULT_PIPELINE_NAME;
const envLocalPath = resolve("skills/.env.local");
const result = {
@@ -55,7 +58,9 @@ try {
const user = env.LANGBOT_E2E_LOGIN_USER || "";
const password = env.LANGBOT_E2E_LOGIN_PASSWORD || DEFAULT_LOCAL_PASSWORD;
if (!user) {
throw new Error("LANGBOT_E2E_LOGIN_USER is required so this setup can create/update the pipeline via backend API.");
throw new Error(
"LANGBOT_E2E_LOGIN_USER is required so this setup can create/update the pipeline via backend API.",
);
}
const auth = await resetAndAuthLocalUser({ backendUrl, user, password });
@@ -81,7 +86,8 @@ try {
await upsertEnvLocal(envLocalPath, {
LANGBOT_E2E_LOGIN_USER: user,
LANGBOT_QA_AGENT_RUNNER_PIPELINE_URL: result.pipeline_url,
LANGBOT_QA_AGENT_RUNNER_PIPELINE_NAME: result.pipeline_name || pipelineName,
LANGBOT_QA_AGENT_RUNNER_PIPELINE_NAME:
result.pipeline_name || pipelineName,
});
result.wrote_env = true;
}
@@ -92,10 +98,20 @@ try {
console.log(JSON.stringify(result, null, 2));
}
process.exit(result.status === "pass" ? 0 : result.status === "env_issue" ? 2 : 1);
process.exit(
result.status === "pass" ? 0 : result.status === "env_issue" ? 2 : 1,
);
async function ensurePipeline({ backendUrl, token, pipelineName, runnerId, runnerConfig }) {
const pipelineList = await apiJson(backendUrl, "/api/v1/pipelines", { token });
async function ensurePipeline({
backendUrl,
token,
pipelineName,
runnerId,
runnerConfig,
}) {
const pipelineList = await apiJson(backendUrl, "/api/v1/pipelines", {
token,
});
if (isApiFailure(pipelineList)) {
return {
status: "fail",
@@ -114,7 +130,8 @@ async function ensurePipeline({ backendUrl, token, pipelineName, runnerId, runne
token,
body: {
name: pipelineName,
description: "Local QA pipeline for deterministic QA AgentRunner Debug Chat smoke tests.",
description:
"Local QA pipeline for deterministic QA AgentRunner Debug Chat smoke tests.",
emoji: "QA",
},
});
@@ -126,7 +143,11 @@ async function ensurePipeline({ backendUrl, token, pipelineName, runnerId, runne
};
}
const pipelineId = createdResponse.json.data?.uuid || "";
const loaded = await apiJson(backendUrl, `/api/v1/pipelines/${encodeURIComponent(pipelineId)}`, { token });
const loaded = await apiJson(
backendUrl,
`/api/v1/pipelines/${encodeURIComponent(pipelineId)}`,
{ token },
);
pipeline = loaded.json.data?.pipeline || null;
created = true;
}
@@ -138,7 +159,11 @@ async function ensurePipeline({ backendUrl, token, pipelineName, runnerId, runne
};
}
const loaded = await apiJson(backendUrl, `/api/v1/pipelines/${encodeURIComponent(pipeline.uuid)}`, { token });
const loaded = await apiJson(
backendUrl,
`/api/v1/pipelines/${encodeURIComponent(pipeline.uuid)}`,
{ token },
);
if (isApiFailure(loaded) || !loaded.json.data?.pipeline) {
return {
status: "fail",
@@ -149,9 +174,15 @@ async function ensurePipeline({ backendUrl, token, pipelineName, runnerId, runne
}
pipeline = loaded.json.data.pipeline;
const config = pipeline.config && typeof pipeline.config === "object" ? pipeline.config : {};
const config =
pipeline.config && typeof pipeline.config === "object"
? pipeline.config
: {};
const ai = config.ai && typeof config.ai === "object" ? config.ai : {};
const runnerConfigs = ai.runner_config && typeof ai.runner_config === "object" ? ai.runner_config : {};
const runnerConfigs =
ai.runner_config && typeof ai.runner_config === "object"
? ai.runner_config
: {};
const updatedConfig = {
...config,
ai: {
@@ -168,16 +199,21 @@ async function ensurePipeline({ backendUrl, token, pipelineName, runnerId, runne
},
};
const updateResponse = await apiJson(backendUrl, `/api/v1/pipelines/${encodeURIComponent(pipeline.uuid)}`, {
method: "PUT",
token,
body: {
name: pipelineName,
description: "Local QA pipeline for deterministic QA AgentRunner Debug Chat smoke tests.",
emoji: "QA",
config: updatedConfig,
const updateResponse = await apiJson(
backendUrl,
`/api/v1/pipelines/${encodeURIComponent(pipeline.uuid)}`,
{
method: "PUT",
token,
body: {
name: pipelineName,
description:
"Local QA pipeline for deterministic QA AgentRunner Debug Chat smoke tests.",
emoji: "QA",
config: updatedConfig,
},
},
});
);
if (isApiFailure(updateResponse)) {
return {
status: "fail",
@@ -189,7 +225,9 @@ async function ensurePipeline({ backendUrl, token, pipelineName, runnerId, runne
return {
status: "pass",
reason: created ? "QA AgentRunner pipeline created and configured." : "QA AgentRunner pipeline updated.",
reason: created
? "QA AgentRunner pipeline created and configured."
: "QA AgentRunner pipeline updated.",
pipeline_id: pipeline.uuid,
pipeline_name: pipelineName,
created,
@@ -198,7 +236,12 @@ async function ensurePipeline({ backendUrl, token, pipelineName, runnerId, runne
}
function isApiFailure(response) {
return response.status >= 400 || (response.json && response.json.code !== undefined && response.json.code !== 0);
return (
response.status >= 400 ||
(response.json &&
response.json.code !== undefined &&
response.json.code !== 0)
);
}
async function upsertEnvLocal(path, values) {