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
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:
@@ -0,0 +1,113 @@
|
||||
package chatpipeline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/Tencent/WeKnora/internal/infrastructure/web_fetch"
|
||||
"github.com/Tencent/WeKnora/internal/logger"
|
||||
"github.com/Tencent/WeKnora/internal/types"
|
||||
)
|
||||
|
||||
// PluginWebFetch fetches full page content for reranked web search results.
|
||||
// It runs between CHUNK_RERANK and CHUNK_MERGE, replacing snippet content
|
||||
// with the full page text for the top N web results.
|
||||
type PluginWebFetch struct{}
|
||||
|
||||
// NewPluginWebFetch creates and registers a new PluginWebFetch instance
|
||||
func NewPluginWebFetch(eventManager *EventManager) *PluginWebFetch {
|
||||
res := &PluginWebFetch{}
|
||||
eventManager.Register(res)
|
||||
return res
|
||||
}
|
||||
|
||||
// ActivationEvents returns the event types this plugin handles
|
||||
func (p *PluginWebFetch) ActivationEvents() []types.EventType {
|
||||
return []types.EventType{types.WEB_FETCH}
|
||||
}
|
||||
|
||||
// OnEvent handles the WEB_FETCH event
|
||||
func (p *PluginWebFetch) OnEvent(
|
||||
ctx context.Context,
|
||||
eventType types.EventType,
|
||||
chatManage *types.ChatManage,
|
||||
next func() *PluginError,
|
||||
) *PluginError {
|
||||
if !chatManage.WebFetchEnabled || !chatManage.WebSearchEnabled {
|
||||
pipelineInfo(ctx, "WebFetch", "skip", map[string]any{"reason": "disabled"})
|
||||
return next()
|
||||
}
|
||||
|
||||
topN := chatManage.WebFetchTopN
|
||||
if topN <= 0 {
|
||||
topN = 3
|
||||
}
|
||||
|
||||
// Find web search results in reranked results
|
||||
var webResults []*types.SearchResult
|
||||
for _, r := range chatManage.RerankResult {
|
||||
if strings.ToLower(r.KnowledgeSource) == "web_search" {
|
||||
webResults = append(webResults, r)
|
||||
if len(webResults) >= topN {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(webResults) == 0 {
|
||||
pipelineInfo(ctx, "WebFetch", "skip", map[string]any{"reason": "no_web_results"})
|
||||
return next()
|
||||
}
|
||||
|
||||
logger.Infof(ctx, "[PIPELINE] stage=WebFetch action=start count=%d", len(webResults))
|
||||
|
||||
// Fetch in parallel
|
||||
type fetchResult struct {
|
||||
idx int
|
||||
content string
|
||||
err error
|
||||
}
|
||||
results := make([]fetchResult, len(webResults))
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for i, r := range webResults {
|
||||
wg.Add(1)
|
||||
go func(idx int, sr *types.SearchResult) {
|
||||
defer wg.Done()
|
||||
fetchURL := sr.ID // web search results use URL as ID
|
||||
if fetchURL == "" {
|
||||
return
|
||||
}
|
||||
content, err := web_fetch.FetchURLContent(ctx, fetchURL)
|
||||
results[idx] = fetchResult{idx: idx, content: content, err: err}
|
||||
}(i, r)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
// Replace snippet content with fetched full content
|
||||
fetchedCount := 0
|
||||
for _, fr := range results {
|
||||
if fr.err != nil {
|
||||
logger.Warnf(ctx, "[PIPELINE] stage=WebFetch action=fetch_failed url=%s err=%v",
|
||||
webResults[fr.idx].ID, fr.err)
|
||||
continue
|
||||
}
|
||||
if fr.content == "" {
|
||||
continue
|
||||
}
|
||||
// Truncate to reasonable size for LLM context
|
||||
content := fr.content
|
||||
if len(content) > 8000 {
|
||||
content = content[:8000] + "\n...(truncated)"
|
||||
}
|
||||
webResults[fr.idx].Content = content
|
||||
fetchedCount++
|
||||
}
|
||||
|
||||
pipelineInfo(ctx, "WebFetch", "complete", map[string]any{
|
||||
"fetched": fetchedCount,
|
||||
"total": len(webResults),
|
||||
})
|
||||
return next()
|
||||
}
|
||||
Reference in New Issue
Block a user