Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Entry Layer: process entry points.

`umodel-server` and `umodel-mcp` both support `--graphstore`:

When `--graphstore` is omitted, Go entrypoints use `local.ladybug`. Builds without `-tags ladybug` report clear startup guidance: build with the Ladybug tag, or pass `--graphstore file.memory` for local development.

| Provider | Description |
|---|---|
| `memory` | In-memory provider for fast local verification; data is lost on process exit. |
Expand Down
2 changes: 2 additions & 0 deletions cmd/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Entry Layer — 二进制入口点。

`umodel-server` 和 `umodel-mcp` 都支持 `--graphstore`:

未显式传入 `--graphstore` 时,Go 入口默认使用 `local.ladybug`。如果构建时没有 `-tags ladybug`,启动错误会提示两条路径:使用 Ladybug tag 构建,或在本地开发时传入 `--graphstore file.memory`。

| Provider | 说明 |
|---|---|
| `memory` | 纯内存,适合本地快速验证,进程退出后数据丢失;通过纯 Go 引擎支持 Ladybug 兼容只读 Cypher。 |
Expand Down
24 changes: 24 additions & 0 deletions cmd/umodel-mcp/main_no_ladybug_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//go:build !ladybug

package main

import (
"bytes"
"strings"
"testing"
)

func TestRunDefaultLadybugUnavailableGuidesLocalDevelopment(t *testing.T) {
var out bytes.Buffer
var errOut bytes.Buffer

err := run([]string{"--manifest", "--data", t.TempDir()}, strings.NewReader(""), &out, &errOut)
if err == nil {
t.Fatal("expected default local.ladybug stub to be unavailable in no-ladybug build")
}
for _, want := range []string{"local.ladybug", "-tags ladybug", "--graphstore file.memory"} {
if !strings.Contains(err.Error(), want) {
t.Fatalf("expected %q in error %q", want, err.Error())
}
}
}
2 changes: 2 additions & 0 deletions docs/en/graphstore-providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

UModel stores UModel elements, CMS 2.0 entities, and topology relations behind the `GraphStore` interface. Go entry binaries default to `local.ladybug` when `--graphstore` is omitted; select another provider with `--graphstore` on `umodel-server` or `umodel-mcp`.

If a build omits the `ladybug` tag, the default `local.ladybug` provider reports an unavailable health status. Build with `-tags ladybug` to enable the real `local.ladybug` provider, or pass `--graphstore file.memory` for local development without Ladybug.

```bash
go run ./cmd/umodel-server --addr :8080 --data data --graphstore file.memory
```
Expand Down
2 changes: 2 additions & 0 deletions docs/zh/graphstore-providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ English: [GraphStore Providers](../en/graphstore-providers.md)
UModel 通过 `GraphStore` 接口保存和查询 UModel elements、CMS 2.0 实体以及拓扑关系。运行时通过 `--graphstore` 选择 provider。


未显式传入 `--graphstore` 时,Go 入口默认使用 `local.ladybug`。如果构建时没有 `-tags ladybug`,该 provider 会报告不可用状态。使用 `-tags ladybug` 构建即可启用真实的 `local.ladybug` provider;本地开发且不使用 Ladybug 时请显式传入 `--graphstore file.memory`。

## Providers

| Provider | 持久化 | 典型用途 |
Expand Down
6 changes: 4 additions & 2 deletions internal/graphstore/provider/ladybug/provider_stub.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (

type Provider struct{}

const noLadybugBuildGuidance = "build with -tags ladybug to enable local.ladybug, or run with --graphstore file.memory for local development"

func NewProvider(config graphstore.ProviderConfig) (*Provider, error) {
return &Provider{}, nil
}
Expand Down Expand Up @@ -64,11 +66,11 @@ func (p *Provider) Capabilities(ctx context.Context) (model.GraphStoreCapabiliti
}

func (p *Provider) Health(ctx context.Context) (model.GraphStoreHealth, error) {
return model.GraphStoreHealth{Provider: graphstore.ProviderTypeLadybug, Status: "unavailable", Message: "build with -tags ladybug to enable local.ladybug"}, nil
return model.GraphStoreHealth{Provider: graphstore.ProviderTypeLadybug, Status: "unavailable", Message: noLadybugBuildGuidance}, nil
}

func unavailable() error {
return apperrors.New(apperrors.CodeProviderUnavailable, "local.ladybug provider is disabled in this build")
return apperrors.New(apperrors.CodeProviderUnavailable, "local.ladybug provider is disabled in this build; "+noLadybugBuildGuidance)
}

func ladybugCapabilities() model.GraphStoreCapabilities {
Expand Down
16 changes: 16 additions & 0 deletions internal/graphstore/provider/ladybug/provider_stub_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package ladybug

import (
"context"
"strings"
"testing"

"github.com/alibaba/UnifiedModel/internal/graphstore"
Expand All @@ -20,9 +21,13 @@ func TestStubProviderReportsUnavailable(t *testing.T) {
ctx := context.Background()
if health, err := provider.Health(ctx); err != nil || health.Status != "unavailable" || health.Provider != graphstore.ProviderTypeLadybug {
t.Fatalf("health: %+v err=%v", health, err)
} else {
requireLadybugGuidance(t, health.Message)
}
if err := provider.OpenWorkspace(ctx, model.WorkspaceMetadata{ID: "demo"}); !apperrors.IsCode(err, apperrors.CodeProviderUnavailable) {
t.Fatalf("expected provider unavailable, got %v", err)
} else {
requireLadybugGuidance(t, err.Error())
}
if capabilities, err := provider.Capabilities(ctx); err != nil || !capabilities.ControlledCypher || capabilities.MaxLimit == 0 {
t.Fatalf("stub should expose intended local.ladybug capabilities for planning, got %+v err=%v", capabilities, err)
Expand All @@ -36,5 +41,16 @@ func TestStubProviderIsRegisteredButUnavailable(t *testing.T) {
}
if err := provider.EnsureSchema(context.Background(), "demo"); !apperrors.IsCode(err, apperrors.CodeProviderUnavailable) {
t.Fatalf("expected registered stub provider to be unavailable, got %v", err)
} else {
requireLadybugGuidance(t, err.Error())
}
}

func requireLadybugGuidance(t *testing.T, message string) {
t.Helper()
for _, want := range []string{"-tags ladybug", "--graphstore file.memory"} {
if !strings.Contains(message, want) {
t.Fatalf("expected %q guidance in %q", want, message)
}
}
}
Loading