mirror of
https://github.com/RuoJi6/ARTEX.git
synced 2026-10-30 10:03:50 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
524f8bc632 |
No files matched your search
@@ -12,7 +12,6 @@ import (
|
||||
// Missing host/setting means off. Rechecked on every write for running sessions.
|
||||
var IntelligenceEnabled func() bool
|
||||
var IntelligenceRecord func(context.Context, int64, int64, string, db.IntelligenceInput) (int64, error)
|
||||
var CredentialRecorded func()
|
||||
|
||||
func intelligenceEnabled() bool { return IntelligenceEnabled != nil && IntelligenceEnabled() }
|
||||
func intelligenceSchema() map[string]any {
|
||||
|
||||
+16
-17
@@ -39,23 +39,22 @@ type CredentialTraffic struct {
|
||||
Accounts []int `json:"accounts"`
|
||||
}
|
||||
type CredentialAnalysis struct {
|
||||
ConversationID int64 `json:"conversation_id,omitempty"`
|
||||
ForceExtract bool `json:"force_extract,omitempty"`
|
||||
TrafficNote string `json:"traffic_note,omitempty"`
|
||||
State string `json:"state"`
|
||||
Lease string `json:"lease,omitempty"`
|
||||
LeaseUntil time.Time `json:"lease_until,omitempty"`
|
||||
SourceHash string `json:"source_hash,omitempty"`
|
||||
Method string `json:"method,omitempty"`
|
||||
Accounts []CredentialAccount `json:"accounts"`
|
||||
Traffic []CredentialTraffic `json:"traffic"`
|
||||
TrafficState string `json:"traffic_state"`
|
||||
Note string `json:"note,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Model string `json:"model,omitempty"`
|
||||
InputTokens int `json:"input_tokens"`
|
||||
OutputTokens int `json:"output_tokens"`
|
||||
ProcessedAt *time.Time `json:"processed_at,omitempty"`
|
||||
ForceExtract bool `json:"force_extract,omitempty"`
|
||||
TrafficNote string `json:"traffic_note,omitempty"`
|
||||
State string `json:"state"`
|
||||
Lease string `json:"lease,omitempty"`
|
||||
LeaseUntil time.Time `json:"lease_until,omitempty"`
|
||||
SourceHash string `json:"source_hash,omitempty"`
|
||||
Method string `json:"method,omitempty"`
|
||||
Accounts []CredentialAccount `json:"accounts"`
|
||||
Traffic []CredentialTraffic `json:"traffic"`
|
||||
TrafficState string `json:"traffic_state"`
|
||||
Note string `json:"note,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Model string `json:"model,omitempty"`
|
||||
InputTokens int `json:"input_tokens"`
|
||||
OutputTokens int `json:"output_tokens"`
|
||||
ProcessedAt *time.Time `json:"processed_at,omitempty"`
|
||||
}
|
||||
|
||||
func CredentialSourceHash(content string) string {
|
||||
|
||||
@@ -233,13 +233,6 @@ func wireTools(pg *db.DB, domainReg map[string]actool.CoreTool) {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if item.Kind == "credential" {
|
||||
// The durable database row is the source of truth; this wake-up only
|
||||
// reduces latency. The recovery ticker handles missed notifications.
|
||||
if agent.CredentialRecorded != nil {
|
||||
agent.CredentialRecorded()
|
||||
}
|
||||
}
|
||||
return item.ID, nil
|
||||
}
|
||||
// Seed the built-in domain tools (first-insert only; DO NOTHING preserves edits).
|
||||
|
||||
+1
-25
@@ -2,7 +2,6 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
@@ -42,16 +41,13 @@ func (s *Server) seedCredentialProcessor() {
|
||||
_ = s.m.pg.SetBool(flag, true)
|
||||
}
|
||||
func (s *Server) runCredentialProcessor() {
|
||||
// Events wake the worker immediately. The slow ticker is only a recovery
|
||||
// scan for rows created before a notification or after a process restart.
|
||||
ticker := time.NewTicker(30 * time.Second)
|
||||
ticker := time.NewTicker(3 * time.Second)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-s.ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
case <-s.credentialWake:
|
||||
}
|
||||
if !s.m.pg.GetBool(db.SettingTaskIntelligence, false) || !s.m.pg.GetBool(db.SettingCredentialAgent, false) {
|
||||
continue
|
||||
@@ -97,14 +93,6 @@ func (s *Server) processCredentialJob(parent context.Context, item *db.Intellige
|
||||
}
|
||||
}()
|
||||
result := *item.Credentials
|
||||
conversationID := result.ConversationID
|
||||
if conversationID == 0 {
|
||||
if c, err := s.m.pg.CreateConversation(db.CredentialAgentKey, "凭据整理 · "+firstLine(item.Title, 42), nil); err == nil {
|
||||
conversationID = c.ID
|
||||
result.ConversationID = c.ID
|
||||
_, _ = s.m.pg.AppendConvActivity(c.ID, db.Activity{Worker: db.CredentialAgentKey, Kind: "user", Summary: "收到凭据上报,开始整理", Detail: item.Content})
|
||||
}
|
||||
}
|
||||
needsExtract := result.ForceExtract || result.SourceHash != db.CredentialSourceHash(item.Content) || result.Method == "" || (result.Error != "" && len(result.Accounts) == 0)
|
||||
result.State = "ready"
|
||||
result.ForceExtract = false
|
||||
@@ -197,20 +185,8 @@ func (s *Server) processCredentialJob(parent context.Context, item *db.Intellige
|
||||
if err := s.m.pg.FinishCredentialJob(finish, item, result); err != nil {
|
||||
log.Printf("[credentials] save record #%d: %v", item.ID, err)
|
||||
}
|
||||
if conversationID > 0 {
|
||||
detail, _ := json.Marshal(result)
|
||||
kind, summary := "result", "凭据整理完成"
|
||||
isError := result.State == "error"
|
||||
if isError {
|
||||
kind, summary = "text", "凭据整理失败:"+result.Error
|
||||
}
|
||||
_, _ = s.m.pg.AppendConvActivity(conversationID, db.Activity{Worker: db.CredentialAgentKey, Kind: kind, IsError: isError, Summary: summary, Detail: string(detail), InputTokens: intPtr(result.InputTokens), OutputTokens: intPtr(result.OutputTokens)})
|
||||
_ = s.m.pg.TouchConversation(conversationID)
|
||||
}
|
||||
}
|
||||
|
||||
func intPtr(v int) *int { return &v }
|
||||
|
||||
var credentialURLs = regexp.MustCompile(`https?://[^\s<>"',;))]+`)
|
||||
var credentialTrafficIDs = regexp.MustCompile(`\b[0-9]{8,}-[0-9]{3,}\b`)
|
||||
|
||||
|
||||
+4
-11
@@ -125,10 +125,9 @@ type Server struct {
|
||||
|
||||
// Cold task archives run through one persistent FIFO worker. The buffered wake
|
||||
// channel coalesces enqueue bursts; the database remains the source of truth.
|
||||
archiveWake chan struct{}
|
||||
archiveWG sync.WaitGroup
|
||||
credentialWake chan struct{}
|
||||
side *sideQuestionState
|
||||
archiveWake chan struct{}
|
||||
archiveWG sync.WaitGroup
|
||||
side *sideQuestionState
|
||||
}
|
||||
|
||||
// provEntry is a cached provider + its config for one LLM profile id.
|
||||
@@ -162,7 +161,7 @@ func New(ctx context.Context, m *Manager, skillDir string, dataDir string, keyDi
|
||||
triggerActive: map[string]int{}, triggerCfg: map[string]triggerBehavior{},
|
||||
profChatAgents: map[int64]*agent.ChatAgent{},
|
||||
provByProfile: map[int64]*provEntry{}, llmHealth: newLLMHealthRegistry(m.pg),
|
||||
taskAgents: map[string]*taskAgentBundle{}, archiveWake: make(chan struct{}, 1), credentialWake: make(chan struct{}, 1)}
|
||||
taskAgents: map[string]*taskAgentBundle{}, archiveWake: make(chan struct{}, 1)}
|
||||
s.initSideQuestions()
|
||||
if m.pg != nil {
|
||||
if err := s.basicAuth.load(m.pg); err != nil {
|
||||
@@ -283,12 +282,6 @@ func New(ctx context.Context, m *Manager, skillDir string, dataDir string, keyDi
|
||||
go s.reconcileConcurrency()
|
||||
s.startTaskArchiveWorker()
|
||||
if m.pg != nil {
|
||||
agent.CredentialRecorded = func() {
|
||||
select {
|
||||
case s.credentialWake <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
s.seedCredentialProcessor()
|
||||
go s.runCredentialProcessor()
|
||||
}
|
||||
|
||||
@@ -923,7 +923,7 @@ export default function FindingsPage() {
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="flex min-w-0 flex-nowrap items-center gap-2 overflow-x-auto pb-1">
|
||||
<div className="scrollbar-none flex min-w-0 flex-nowrap items-center gap-2 overflow-x-auto pb-1">
|
||||
<InputGroup className="w-72 shrink-0">
|
||||
<InputGroupInput
|
||||
type="search"
|
||||
|
||||
@@ -112,7 +112,7 @@ export function RepeaterWorkspace() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex min-h-10 min-w-0 items-center rounded-md border bg-muted/20 p-1">
|
||||
<div className="flex min-w-0 flex-1 items-center gap-1 overflow-x-auto">
|
||||
<div className="scrollbar-none flex min-w-0 flex-1 items-center gap-1 overflow-x-auto">
|
||||
{workspace.tabs.map((tab) => {
|
||||
const selected = tab.id === workspace.activeTabId;
|
||||
return (
|
||||
|
||||
@@ -14,7 +14,7 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
|
||||
data-slot="input-group"
|
||||
role="group"
|
||||
className={cn(
|
||||
"group/input-group relative flex h-8 w-full min-w-0 items-center rounded-lg border border-input transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5",
|
||||
"group/input-group relative flex h-8 w-full min-w-0 items-center rounded-lg border border-input transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-1 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/30 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
Reference in new issue
Block a user