WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s

This commit is contained in:
yawan
2026-09-22 16:19:06 +08:00
commit f7fc992f57
3929 changed files with 1006607 additions and 0 deletions
+73
View File
@@ -0,0 +1,73 @@
package main
import (
"context"
"fmt"
"strconv"
"sync"
"github.com/Tencent/WeKnora/internal/models/requestbudget"
)
func probeSendLimit(requested *int, environment string, envSet bool) (int, error) {
limit := requestbudget.GraphMaxSends
if requested != nil {
limit = *requested
}
if limit < 1 || limit > requestbudget.GraphMaxSends {
return 0, fmt.Errorf("invalid probe HTTP send cap")
}
if envSet && environment != strconv.Itoa(limit) {
return 0, fmt.Errorf("probe HTTP send cap differs between request and environment")
}
return limit, nil
}
// This tighter probe allowance delegates model, phase and deadline enforcement
// to the existing graph guard; worker construction and defaults are untouched.
type probeExecutionBudget struct {
*requestbudget.GraphExecution
mu sync.Mutex
limit int
stopped error
cancel context.CancelCauseFunc
}
func bindProbeExecution(ctx context.Context, key, model string, limit int) (context.Context, *probeExecutionBudget, context.CancelFunc, error) {
if _, err := probeSendLimit(&limit, "", false); err != nil {
return ctx, nil, func() {}, err
}
ctx, base, baseCancel, err := requestbudget.BindGraphExecution(ctx, key, model)
if err != nil {
return ctx, nil, baseCancel, err
}
ctx, cancel := context.WithCancelCause(ctx)
guard := &probeExecutionBudget{GraphExecution: base, limit: limit, cancel: cancel}
return requestbudget.WithGuard(ctx, guard), guard, func() { cancel(nil); baseCancel() }, nil
}
func (g *probeExecutionBudget) Check(ctx context.Context) error {
g.mu.Lock()
defer g.mu.Unlock()
if g.stopped != nil {
return g.stopped
}
return g.GraphExecution.Check(ctx)
}
func (g *probeExecutionBudget) Reserve(ctx context.Context, model, phase string) error {
g.mu.Lock()
defer g.mu.Unlock()
if g.stopped != nil {
return g.stopped
}
if err := g.GraphExecution.Check(ctx); err != nil {
return err
}
if g.Reservations() >= g.limit {
g.stopped = fmt.Errorf("%w: probe HTTP send cap reached", requestbudget.ErrStopped)
g.cancel(g.stopped)
return g.stopped
}
return g.GraphExecution.Reserve(ctx, model, phase)
}
@@ -0,0 +1,196 @@
package main
import (
"context"
"errors"
"fmt"
"io"
"net/http"
"strings"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/Tencent/WeKnora/internal/models/requestbudget"
)
func TestProbeSendLimit(t *testing.T) {
one, two, three, zero, four := 1, 2, 3, 0, 4
for _, tc := range []struct {
name string
requested *int
environment string
envSet bool
want int
}{
{name: "default", want: 3},
{name: "minimum", requested: &one, want: 1},
{name: "two", requested: &two, environment: "2", envSet: true, want: 2},
{name: "explicit default", requested: &three, environment: "3", envSet: true, want: 3},
{name: "default environment", environment: "3", envSet: true, want: 3},
{name: "zero", requested: &zero},
{name: "excess", requested: &four},
{name: "environment mismatch", requested: &two, environment: "3", envSet: true},
{name: "environment without request", environment: "2", envSet: true},
{name: "empty environment", requested: &two, envSet: true},
{name: "padded environment", requested: &two, environment: "02", envSet: true},
{name: "decimal environment", requested: &two, environment: "2.0", envSet: true},
{name: "whitespace environment", requested: &two, environment: " 2", envSet: true},
} {
t.Run(tc.name, func(t *testing.T) {
got, err := probeSendLimit(tc.requested, tc.environment, tc.envSet)
if got != tc.want || (err != nil) != (tc.want == 0) {
t.Fatalf("limit = %d, error = %v; want %d", got, err, tc.want)
}
})
}
}
type probeRoundTripper func(*http.Request) (*http.Response, error)
func (f probeRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { return f(req) }
func TestProbeTransportHonorsActualSendCap(t *testing.T) {
for _, limit := range []int{1, 2, requestbudget.GraphMaxSends} {
t.Run(fmt.Sprint(limit), func(t *testing.T) {
ctx, guard, cancel, err := bindProbeExecution(context.Background(), "offline", "model", limit)
if err != nil {
t.Fatal(err)
}
defer cancel()
ctx = requestbudget.WithModel(ctx, "model")
sends := 0
transport := requestbudget.Transport{Base: probeRoundTripper(func(req *http.Request) (*http.Response, error) {
sends++
if req.Context().Err() != nil {
t.Fatal("allowed response was canceled")
}
return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(strings.NewReader("usable response"))}, nil
})}
for i := 0; i < limit; i++ {
req, _ := http.NewRequestWithContext(ctx, http.MethodPost, "https://offline.invalid", nil)
response, err := transport.RoundTrip(req)
if err != nil {
t.Fatalf("send %d: %v", i+1, err)
}
body, readErr := io.ReadAll(response.Body)
response.Body.Close()
if readErr != nil || string(body) != "usable response" || requestbudget.Check(ctx) != nil {
t.Fatal("last allowed response must remain usable")
}
}
req, _ := http.NewRequestWithContext(ctx, http.MethodPost, "https://offline.invalid", nil)
if _, err := transport.RoundTrip(req); !errors.Is(err, requestbudget.ErrStopped) {
t.Fatalf("excess send error = %v", err)
}
if sends != limit || guard.Reservations() != limit || !errors.Is(context.Cause(ctx), requestbudget.ErrStopped) {
t.Fatalf("sends = %d, reservations = %d, cause = %v", sends, guard.Reservations(), context.Cause(ctx))
}
if err := guard.Reserve(context.Background(), "model", requestbudget.GraphPhase); !errors.Is(err, requestbudget.ErrStopped) {
t.Fatal("stopped execution was revived")
}
})
}
}
func TestProbeBudgetPreservesScopeAndDeadline(t *testing.T) {
for _, tc := range []struct{ name, model, phase string }{
{"model", "other", requestbudget.GraphPhase},
{"phase", "model", "other"},
} {
t.Run(tc.name, func(t *testing.T) {
ctx, guard, cancel, err := bindProbeExecution(context.Background(), "offline", "model", 2)
if err != nil {
t.Fatal(err)
}
defer cancel()
if err := guard.Reserve(ctx, tc.model, tc.phase); !errors.Is(err, requestbudget.ErrStopped) {
t.Fatalf("scope mismatch error = %v", err)
}
if guard.Reservations() != 0 || !errors.Is(context.Cause(ctx), requestbudget.ErrStopped) {
t.Fatal("scope mismatch consumed a send or did not stop execution")
}
})
}
t.Run("inherited deadline", func(t *testing.T) {
deadline := time.Now().Add(time.Minute)
parent, parentCancel := context.WithDeadline(context.Background(), deadline)
defer parentCancel()
ctx, guard, cancel, err := bindProbeExecution(parent, "offline", "model", 2)
if err != nil {
t.Fatal(err)
}
defer cancel()
if got, ok := ctx.Deadline(); !ok || !got.Equal(deadline) {
t.Fatal("inherited deadline was reset")
}
if err := guard.Reserve(ctx, "model", requestbudget.GraphPhase); err != nil {
t.Fatal(err)
}
if got, _ := ctx.Deadline(); !got.Equal(deadline) {
t.Fatal("reservation reset the deadline")
}
parentCancel()
if err := guard.Reserve(ctx, "model", requestbudget.GraphPhase); !errors.Is(err, context.Canceled) {
t.Fatalf("cancellation error = %v", err)
}
if guard.Reservations() != 1 {
t.Fatal("canceled send consumed a reservation")
}
})
t.Run("default deadline", func(t *testing.T) {
started := time.Now()
ctx, _, cancel, err := bindProbeExecution(context.Background(), "offline", "model", 2)
if err != nil {
t.Fatal(err)
}
defer cancel()
deadline, ok := ctx.Deadline()
if !ok || deadline.Before(started.Add(requestbudget.GraphTimeout)) || deadline.After(time.Now().Add(requestbudget.GraphTimeout)) {
t.Fatal("probe did not retain the shared absolute timeout")
}
})
t.Run("already canceled", func(t *testing.T) {
parent, cancel := context.WithCancel(context.Background())
cancel()
if _, _, cleanup, err := bindProbeExecution(parent, "offline", "model", 2); err == nil {
cleanup()
t.Fatal("canceled execution was rebound")
}
})
t.Run("nested execution", func(t *testing.T) {
ctx, _, cancel, err := bindProbeExecution(context.Background(), "offline", "model", 2)
if err != nil {
t.Fatal(err)
}
defer cancel()
if _, _, cleanup, err := bindProbeExecution(ctx, "retry", "model", 2); err == nil {
cleanup()
t.Fatal("nested execution reset the allowance")
}
})
}
func TestProbeBudgetConcurrentReservationsStayBounded(t *testing.T) {
ctx, guard, cancel, err := bindProbeExecution(context.Background(), "offline", "model", 2)
if err != nil {
t.Fatal(err)
}
defer cancel()
var successful atomic.Int32
var group sync.WaitGroup
for i := 0; i < 16; i++ {
group.Add(1)
go func() {
defer group.Done()
if guard.Reserve(ctx, "model", requestbudget.GraphPhase) == nil {
successful.Add(1)
}
}()
}
group.Wait()
if successful.Load() != 2 || guard.Reservations() != 2 {
t.Fatalf("successful = %d; reservations = %d", successful.Load(), guard.Reservations())
}
}
+170
View File
@@ -0,0 +1,170 @@
// graph_extraction_probe calls the production extractor without writing graph data.
// The model configuration and AES key arrive on stdin and are never reported.
package main
import (
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"time"
"unicode/utf8"
chatpipeline "github.com/Tencent/WeKnora/internal/application/service/chat_pipeline"
"github.com/Tencent/WeKnora/internal/models/chat"
"github.com/Tencent/WeKnora/internal/types"
"github.com/Tencent/WeKnora/internal/utils"
"gopkg.in/yaml.v3"
)
type request struct {
ExecutionID string `json:"execution_id"`
MaxHTTPSends *int `json:"max_http_sends"`
AESKey string `json:"aes_key"`
Model types.Model `json:"model"`
Chunk types.Chunk `json:"chunk"`
ExtractConfig types.ExtractConfig `json:"extract_config"`
}
type chatDelegate interface{ chat.Chat }
type probeCall struct {
Number int `json:"number"`
ElapsedMS int64 `json:"elapsed_ms"`
MaxTokens int `json:"max_tokens"`
Thinking *bool `json:"thinking_requested"`
ResponseReceived bool `json:"response_received"`
Failed bool `json:"failed"`
FinishReason string `json:"finish_reason"`
ContentRunes int `json:"content_runes"`
ReasoningRunes int `json:"reasoning_runes"`
Usage types.TokenUsage `json:"usage"`
}
type recordingChat struct {
chatDelegate
response *types.ChatResponse
calls int
records []probeCall
}
func (c *recordingChat) Chat(ctx context.Context, messages []chat.Message, options *chat.ChatOptions) (*types.ChatResponse, error) {
started := time.Now()
response, err := c.chatDelegate.Chat(ctx, messages, options)
c.response = response
c.calls++
record := probeCall{
Number: c.calls, ElapsedMS: time.Since(started).Milliseconds(),
ResponseReceived: response != nil, Failed: err != nil,
}
if options != nil {
record.MaxTokens = options.MaxTokens
if options.Thinking != nil {
thinking := *options.Thinking
record.Thinking = &thinking
}
}
if response != nil {
record.FinishReason = response.FinishReason
record.ContentRunes = utf8.RuneCountInString(response.Content)
record.ReasoningRunes = utf8.RuneCountInString(response.ReasoningContent)
record.Usage = response.Usage
}
c.records = append(c.records, record)
return response, err
}
func run() error {
var input request
if err := json.NewDecoder(os.Stdin).Decode(&input); err != nil {
return fmt.Errorf("invalid probe input")
}
capValue, capSet := os.LookupEnv("GRAPH_PROBE_MAX_SENDS")
maxSends, err := probeSendLimit(input.MaxHTTPSends, capValue, capSet)
if err != nil {
return err
}
if input.ExecutionID == "" || input.Chunk.ID == "" || input.Chunk.Content == "" || input.Model.ID == "" || input.Model.Name == "" {
return fmt.Errorf("probe requires a source chunk and model")
}
if err := os.Setenv("SYSTEM_AES_KEY", input.AESKey); err != nil {
return err
}
input.AESKey = ""
key, err := utils.DecryptStoredSecret(input.Model.Parameters.APIKey)
if err != nil {
return fmt.Errorf("cannot decrypt configured model credential")
}
input.Model.Parameters.APIKey = key
defer os.Unsetenv("SYSTEM_AES_KEY")
chatConfig := chat.ConfigFromModel(&input.Model, "", "")
model, err := chat.NewChat(chatConfig, nil)
if err != nil {
return fmt.Errorf("cannot initialize configured remote model")
}
base, err := os.ReadFile("config/config.yaml")
if err != nil {
return err
}
var config struct {
Extract struct {
Graph struct {
Description string `yaml:"description"`
} `yaml:"extract_graph"`
} `yaml:"extract"`
}
if err := yaml.Unmarshal(base, &config); err != nil {
return err
}
recorder := &recordingChat{chatDelegate: model}
extractor := chatpipeline.NewGraphExtractor(recorder, &types.PromptTemplateStructured{
Description: types.AppendCustomPromptInstructions(config.Extract.Graph.Description, input.ExtractConfig.CustomInstructions, "graph_extraction"),
Tags: input.ExtractConfig.Tags,
})
ctx, guard, cancel, err := bindProbeExecution(context.Background(), input.ExecutionID, input.Model.ID, maxSends)
if err != nil {
return err
}
defer cancel()
started := time.Now()
graph, extractionErr := extractor.Extract(ctx, input.Chunk.Content)
hash := sha256.Sum256([]byte(input.Chunk.Content))
report := map[string]any{
"checked_at": started.UTC().Format(time.RFC3339), "elapsed_ms": time.Since(started).Milliseconds(),
"chunk_id": input.Chunk.ID, "source_sha256": hex.EncodeToString(hash[:]),
"model_name": input.Model.Name, "allowed_relation_types": input.ExtractConfig.Tags,
"passed": extractionErr == nil, "graph": graph, "production_write": false,
"model_calls": recorder.calls,
"execution_id": input.ExecutionID,
"http_send_reservations": guard.Reservations(),
"http_send_limit": maxSends,
"call_diagnostics": recorder.records,
"effective_thinking_control": chat.EffectiveThinkingControl(chatConfig),
"diagnostics_version": 2,
}
if recorder.response != nil {
report["finish_reason"] = recorder.response.FinishReason
report["usage"] = recorder.response.Usage
if extractionErr != nil {
report["rejected_model_content"] = recorder.response.Content
}
}
if extractionErr != nil {
report["error"] = extractionErr.Error()
}
encoded, err := json.Marshal(report)
if err != nil {
return err
}
fmt.Printf("GRAPH_EXTRACTION_PROBE_RESULT=%s\n", encoded)
return nil
}
func main() {
if err := run(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
@@ -0,0 +1,77 @@
package main
import (
"context"
"encoding/json"
"errors"
"strings"
"testing"
"github.com/Tencent/WeKnora/internal/models/chat"
"github.com/Tencent/WeKnora/internal/types"
)
type fakeProbeChat struct {
chatDelegate
response *types.ChatResponse
err error
}
func (f *fakeProbeChat) Chat(context.Context, []chat.Message, *chat.ChatOptions) (*types.ChatResponse, error) {
return f.response, f.err
}
func TestRecordingChatPreservesPerCallDiagnosticsWithoutContent(t *testing.T) {
fake := &fakeProbeChat{response: &types.ChatResponse{
Content: "answer", ReasoningContent: "private reasoning", FinishReason: "length",
Usage: types.TokenUsage{PromptTokens: 10, CompletionTokens: 4, TotalTokens: 14},
}}
recorder := &recordingChat{chatDelegate: fake}
thinking := false
options := &chat.ChatOptions{MaxTokens: 4096, Thinking: &thinking}
first, err := recorder.Chat(context.Background(), nil, options)
if err != nil || first != fake.response {
t.Fatal("recorder changed the response")
}
thinking = true
fake.response = &types.ChatResponse{Content: "\u4e2d\u6587", FinishReason: "stop"}
if _, err := recorder.Chat(context.Background(), nil, options); err != nil {
t.Fatal(err)
}
if recorder.calls != 2 || len(recorder.records) != 2 {
t.Fatal("missing call diagnostics")
}
a, b := recorder.records[0], recorder.records[1]
if a.Number != 1 || a.MaxTokens != 4096 || a.Thinking == nil || *a.Thinking ||
!a.ResponseReceived || a.Failed || a.FinishReason != "length" ||
a.ContentRunes != 6 || a.ReasoningRunes != 17 || a.Usage.TotalTokens != 14 {
t.Fatalf("unexpected first call metadata: %+v", a)
}
if b.Number != 2 || b.Thinking == nil || !*b.Thinking || b.ContentRunes != 2 || b.ReasoningRunes != 0 {
t.Fatalf("unexpected second call metadata: %+v", b)
}
encoded, err := json.Marshal(recorder.records)
if err != nil {
t.Fatal(err)
}
if strings.Contains(string(encoded), "private reasoning") || strings.Contains(string(encoded), "answer") {
t.Fatal("diagnostics exposed response content")
}
}
func TestRecordingChatRecordsNoResponseWithoutLeakingError(t *testing.T) {
want := errors.New("private upstream error")
recorder := &recordingChat{chatDelegate: &fakeProbeChat{err: want}}
response, err := recorder.Chat(context.Background(), nil, nil)
if response != nil || err != want || len(recorder.records) != 1 {
t.Fatal("recorder changed the failure")
}
record := recorder.records[0]
if record.ResponseReceived || !record.Failed || record.Thinking != nil || record.ContentRunes != 0 || record.ReasoningRunes != 0 {
t.Fatalf("unexpected failure metadata: %+v", record)
}
encoded, _ := json.Marshal(record)
if strings.Contains(string(encoded), want.Error()) {
t.Fatal("diagnostics exposed upstream error")
}
}