mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-08-28 05:25:04 +00:00
fix(packaging): protect qwenpaw releases and test Studio health
This commit is contained in:
parent
f986f73ffa
commit
00abb4ef29
7 changed files with 144 additions and 20 deletions
16
.github/workflows/_build-python-packages.yml
vendored
16
.github/workflows/_build-python-packages.yml
vendored
|
|
@ -77,6 +77,22 @@ jobs:
|
|||
assert (static_dir() / "index.html").is_file()
|
||||
PY
|
||||
|
||||
# qwenpaw composes independently released plugins. Keep this before the
|
||||
# artifact upload so a core release cannot advertise an unavailable extra.
|
||||
- name: Verify released qwenpaw dependencies
|
||||
if: inputs.expected_version != ''
|
||||
run: |
|
||||
REME_WHEEL="$(pwd)/$(ls dist/reme/reme_ai-[0-9]*.whl)"
|
||||
python -m venv "${RUNNER_TEMP}/reme-qwenpaw-package-smoke"
|
||||
"${RUNNER_TEMP}/reme-qwenpaw-package-smoke/bin/python" -m pip install "${REME_WHEEL}[qwenpaw]"
|
||||
cd "${RUNNER_TEMP}"
|
||||
"${RUNNER_TEMP}/reme-qwenpaw-package-smoke/bin/python" - <<'PY'
|
||||
from importlib.metadata import distribution
|
||||
|
||||
assert distribution("reme-auto-fin")
|
||||
assert distribution("reme-daily-paper")
|
||||
PY
|
||||
|
||||
- name: Upload ReMe distributions
|
||||
if: inputs.upload_artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
|
|
|
|||
3
.github/workflows/release-python.yml
vendored
3
.github/workflows/release-python.yml
vendored
|
|
@ -1,5 +1,8 @@
|
|||
name: Release / Python packages
|
||||
|
||||
# reme-ai[qwenpaw] is verified before publication. Publish the independently
|
||||
# versioned reme-auto-fin and reme-daily-paper requirements first.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import type {
|
|||
StreamChunk,
|
||||
} from "./types";
|
||||
import { decodeSseEvent } from "./chat-stream";
|
||||
import { healthFromResponse } from "./health-status";
|
||||
import { translate, useLanguageStore, type TranslationKey } from "./i18n";
|
||||
import {
|
||||
WORKSPACE_FILE_LIMIT,
|
||||
|
|
@ -70,10 +71,7 @@ export async function getReMeStatus(): Promise<ReMeResponse<string>> {
|
|||
|
||||
export async function getReMeHealth(): Promise<ReMeHealth | undefined> {
|
||||
const response = await callReMe<string>("health_check");
|
||||
const health = response.metadata.health;
|
||||
return health && typeof health === "object"
|
||||
? (health as ReMeHealth)
|
||||
: undefined;
|
||||
return healthFromResponse(response);
|
||||
}
|
||||
|
||||
export async function rebuildReMeIndex(): Promise<ReMeResponse<unknown>> {
|
||||
|
|
|
|||
42
reme_studio/app/health-status.ts
Normal file
42
reme_studio/app/health-status.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import type { ReMeComponentHealth, ReMeHealth, ReMeResponse } from "./types";
|
||||
|
||||
export interface ComponentMemoryUsage {
|
||||
human?: string;
|
||||
}
|
||||
|
||||
export interface HealthComponentEntry {
|
||||
type: string;
|
||||
name: string;
|
||||
component: ReMeComponentHealth;
|
||||
memory?: string;
|
||||
}
|
||||
|
||||
export function healthFromResponse(
|
||||
response: Pick<ReMeResponse, "metadata">,
|
||||
): ReMeHealth | undefined {
|
||||
const health = response.metadata.health;
|
||||
return health && typeof health === "object"
|
||||
? (health as ReMeHealth)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
export function isComponentHealthy(component: ReMeComponentHealth): boolean {
|
||||
return (
|
||||
component.is_healthy === true ||
|
||||
(component.is_started === true && component.is_healthy !== false)
|
||||
);
|
||||
}
|
||||
|
||||
export function healthComponentEntries(
|
||||
health?: ReMeHealth,
|
||||
memory?: Record<string, Record<string, ComponentMemoryUsage>>,
|
||||
): HealthComponentEntry[] {
|
||||
return Object.entries(health?.components || {}).flatMap(([type, entries]) =>
|
||||
Object.entries(entries).map(([name, component]) => ({
|
||||
type,
|
||||
name,
|
||||
component,
|
||||
memory: memory?.[type]?.[name]?.human,
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ import {
|
|||
rebuildReMeIndex,
|
||||
REME_API_ENDPOINT,
|
||||
} from "./api";
|
||||
import { healthComponentEntries, isComponentHealthy } from "./health-status";
|
||||
import { useI18n, type TranslationKey } from "./i18n";
|
||||
import type {
|
||||
AppConfig,
|
||||
|
|
@ -61,13 +62,6 @@ const COMPONENT_ICONS: Record<string, React.ReactNode> = {
|
|||
keyword_index: <FileArchive size={18} />,
|
||||
};
|
||||
|
||||
function isComponentHealthy(component: ReMeComponentHealth): boolean {
|
||||
return (
|
||||
component.is_healthy === true ||
|
||||
(component.is_started === true && component.is_healthy !== false)
|
||||
);
|
||||
}
|
||||
|
||||
const COMPONENT_LABELS = {
|
||||
embedding_store: "embeddingStore",
|
||||
file_graph: "fileGraph",
|
||||
|
|
@ -256,15 +250,7 @@ export default function SettingsCenter({
|
|||
([type, entries]) =>
|
||||
Object.entries(entries).map(([name, usage]) => ({ type, name, usage })),
|
||||
);
|
||||
const healthComponents = Object.entries(health?.components || {}).flatMap(
|
||||
([type, entries]) =>
|
||||
Object.entries(entries).map(([name, component]) => ({
|
||||
type,
|
||||
name,
|
||||
component,
|
||||
memory: memory?.components?.[type]?.[name]?.human,
|
||||
})),
|
||||
);
|
||||
const healthComponents = healthComponentEntries(health, memory?.components);
|
||||
const healthyComponents = healthComponents.filter(({ component }) =>
|
||||
isComponentHealthy(component),
|
||||
).length;
|
||||
|
|
|
|||
24
reme_studio/tests/api-health.test.mjs
Normal file
24
reme_studio/tests/api-health.test.mjs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { healthFromResponse } from "../app/health-status.ts";
|
||||
|
||||
test("health metadata is returned from the existing health_check response", () => {
|
||||
const health = {
|
||||
version: "0.4.1.8",
|
||||
healthy: true,
|
||||
components: { file_store: { default: { is_started: true } } },
|
||||
};
|
||||
|
||||
const result = healthFromResponse({ metadata: { health } });
|
||||
|
||||
assert.deepEqual(result, health);
|
||||
});
|
||||
|
||||
test("invalid health metadata keeps the settings fallback available", () => {
|
||||
assert.equal(healthFromResponse({ metadata: {} }), undefined);
|
||||
assert.equal(healthFromResponse({ metadata: { health: null } }), undefined);
|
||||
assert.equal(
|
||||
healthFromResponse({ metadata: { health: "unknown" } }),
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
55
reme_studio/tests/health-status.test.mjs
Normal file
55
reme_studio/tests/health-status.test.mjs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import {
|
||||
healthComponentEntries,
|
||||
isComponentHealthy,
|
||||
} from "../app/health-status.ts";
|
||||
|
||||
test("component health honors explicit failures and started fallbacks", () => {
|
||||
assert.equal(isComponentHealthy({ is_healthy: true }), true);
|
||||
assert.equal(
|
||||
isComponentHealthy({ is_started: true, is_healthy: null }),
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
isComponentHealthy({ is_started: true, is_healthy: false }),
|
||||
false,
|
||||
);
|
||||
assert.equal(isComponentHealthy({ is_started: false }), false);
|
||||
});
|
||||
|
||||
test("health components are flattened with matching memory usage", () => {
|
||||
const components = healthComponentEntries(
|
||||
{
|
||||
version: "0.4.1.8",
|
||||
healthy: true,
|
||||
components: {
|
||||
embedding_store: {
|
||||
default: { is_started: true, dimensions: 1024 },
|
||||
},
|
||||
file_graph: {
|
||||
memory: { is_healthy: false, n_nodes: 12 },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
embedding_store: { default: { human: "12 MiB" } },
|
||||
},
|
||||
);
|
||||
|
||||
assert.deepEqual(components, [
|
||||
{
|
||||
type: "embedding_store",
|
||||
name: "default",
|
||||
component: { is_started: true, dimensions: 1024 },
|
||||
memory: "12 MiB",
|
||||
},
|
||||
{
|
||||
type: "file_graph",
|
||||
name: "memory",
|
||||
component: { is_healthy: false, n_nodes: 12 },
|
||||
memory: undefined,
|
||||
},
|
||||
]);
|
||||
assert.deepEqual(healthComponentEntries(), []);
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue