-
Notifications
You must be signed in to change notification settings - Fork 7
feat(runtime): Runtime 层重构与工具并发执行优化 #289
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
aa2786b
Merge remote-tracking branch 'upstream/main'
phantom5099 df2c67d
Merge remote-tracking branch 'upstream/main'
phantom5099 4108e80
Merge remote-tracking branch 'upstream/main'
phantom5099 260a2d3
Merge remote-tracking branch 'refs/remotes/upstream/main'
phantom5099 ede7033
feat(runtime):增加执行监控与死循环拦截
phantom5099 07a4d3b
fix(runtime): harden terminal event and tool parallelism
xgopilot 974e838
Merge pull request #7 from phantom5099/fork-pr-289-1776160731
phantom5099 a316884
fix(runtime): align progress streak semantics and reduce permission H…
xgopilot 3a8a8f1
Merge pull request #8 from phantom5099/fork-pr-289-1776160731
phantom5099 473ede1
fix(runtime): tighten tool execution scheduling and docs alignment
xgopilot 114fa1f
Merge pull request #9 from phantom5099/fork-pr-289-1776165746
phantom5099 cd2f5d6
fix(runtime): address unresolved review findings and simplify paths
xgopilot 1b7ce02
Merge pull request #10 from phantom5099/fork-pr-289-1776165746
phantom5099 6f21d32
Merge branch 'main' into main
phantom5099 269f83d
fix(runtime): repair runtime tests compile errors
xgopilot c059d4d
Merge pull request #11 from phantom5099/fork-pr-289-1776165746
phantom5099 5d2a94f
fix(runtime): preserve ErrMaxLoopReached sentinel on loop-limit exit
xgopilot 080ea05
Merge pull request #12 from phantom5099/fork-pr-289-1776165746
phantom5099 c49c55e
Merge remote-tracking branch 'upstream/main'
phantom5099 811f93f
Merge remote-tracking branch 'origin/main'
phantom5099 50df6ce
fix(runtime): resolve conflicts with origin/main semantics
xgopilot 2695aab
Merge pull request #13 from phantom5099/fork-pr-289-1776165746
phantom5099 1d49f23
Merge remote-tracking branch 'origin/main'
phantom5099 082c6a2
Merge remote-tracking branch 'upstream/main'
phantom5099 0c3a8c0
test(runtime,tui): fix compile regression and cover new branches
xgopilot 45df4e4
Merge pull request #14 from phantom5099/fork-pr-289-1776165746
phantom5099 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -183,7 +183,7 @@ func (p ResolvedProviderConfig) ToRuntimeConfig() provider.RuntimeConfig { | |
| const ( | ||
| OpenAIName = "openai" | ||
| OpenAIDefaultBaseURL = "https://api.openai.com/v1" | ||
| OpenAIDefaultModel = "gpt-4.1" | ||
| OpenAIDefaultModel = "gpt-5.4" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Default OpenAI model changed to |
||
| OpenAIDefaultAPIKeyEnv = "OPENAI_API_KEY" | ||
|
|
||
| GeminiName = "gemini" | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| package controlplane | ||
|
|
||
| import ( | ||
| "context" | ||
| "errors" | ||
| "strings" | ||
| ) | ||
|
|
||
| // StopInput 汇总停止决议所需的信号(可多信号并存,由 DecideStopReason 按优先级表决)。 | ||
| type StopInput struct { | ||
| ContextCanceled bool | ||
| MaxLoopsReached bool | ||
| RunError error | ||
| Success bool | ||
| } | ||
|
|
||
| // DecideStopReason 按固定优先级返回唯一 StopReason:取消 > 达到轮数上限 > 错误 > 成功。 | ||
| func DecideStopReason(in StopInput) (StopReason, string) { | ||
| if in.ContextCanceled { | ||
| return StopReasonCanceled, "" | ||
| } | ||
| if in.MaxLoopsReached { | ||
| if in.RunError != nil { | ||
| return StopReasonMaxLoops, strings.TrimSpace(in.RunError.Error()) | ||
| } | ||
| return StopReasonMaxLoops, "runtime: max loop reached" | ||
| } | ||
| if in.RunError != nil { | ||
| if errors.Is(in.RunError, context.Canceled) { | ||
| return StopReasonCanceled, "" | ||
| } | ||
| return StopReasonError, strings.TrimSpace(in.RunError.Error()) | ||
| } | ||
| if in.Success { | ||
| return StopReasonSuccess, "" | ||
| } | ||
| return StopReasonError, "runtime: stop reason undetermined" | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| package controlplane | ||
|
|
||
| import ( | ||
| "context" | ||
| "errors" | ||
| "testing" | ||
| ) | ||
|
|
||
| func TestDecideStopReasonPriority(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| errSample := errors.New("boom") | ||
| cases := []struct { | ||
| name string | ||
| in StopInput | ||
| reason StopReason | ||
| }{ | ||
| { | ||
| name: "canceled_wins_over_max_loops", | ||
| in: StopInput{ | ||
| ContextCanceled: true, | ||
| MaxLoopsReached: true, | ||
| RunError: errSample, | ||
| }, | ||
| reason: StopReasonCanceled, | ||
| }, | ||
| { | ||
| name: "max_loops_wins_over_error", | ||
| in: StopInput{ | ||
| MaxLoopsReached: true, | ||
| RunError: errSample, | ||
| }, | ||
| reason: StopReasonMaxLoops, | ||
| }, | ||
| { | ||
| name: "error_when_no_max_loop_flag", | ||
| in: StopInput{ | ||
| RunError: errSample, | ||
| }, | ||
| reason: StopReasonError, | ||
| }, | ||
| { | ||
| name: "success", | ||
| in: StopInput{ | ||
| Success: true, | ||
| }, | ||
| reason: StopReasonSuccess, | ||
| }, | ||
| { | ||
| name: "context_canceled_on_error_field", | ||
| in: StopInput{ | ||
| RunError: context.Canceled, | ||
| }, | ||
| reason: StopReasonCanceled, | ||
| }, | ||
| } | ||
|
|
||
| for _, tc := range cases { | ||
| tc := tc | ||
| t.Run(tc.name, func(t *testing.T) { | ||
| t.Parallel() | ||
| got, _ := DecideStopReason(tc.in) | ||
| if got != tc.reason { | ||
| t.Fatalf("DecideStopReason() = %q, want %q", got, tc.reason) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestDecideStopReasonDetails(t *testing.T) { | ||
| t.Parallel() | ||
|
|
||
| reason, detail := DecideStopReason(StopInput{MaxLoopsReached: true}) | ||
| if reason != StopReasonMaxLoops { | ||
| t.Fatalf("reason = %q, want %q", reason, StopReasonMaxLoops) | ||
| } | ||
| if detail != "runtime: max loop reached" { | ||
| t.Fatalf("detail = %q, want default max-loop detail", detail) | ||
| } | ||
|
|
||
| reason, detail = DecideStopReason(StopInput{}) | ||
| if reason != StopReasonError { | ||
| t.Fatalf("reason = %q, want %q", reason, StopReasonError) | ||
| } | ||
| if detail != "runtime: stop reason undetermined" { | ||
| t.Fatalf("detail = %q, want undetermined detail", detail) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| package controlplane | ||
|
|
||
| // PayloadVersion 为 runtime 事件 envelope 的当前协议版本号。 | ||
| const PayloadVersion = 1 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package controlplane | ||
|
|
||
| // Phase 表示单轮 ReAct 内的显式阶段(plan -> execute -> verify)。 | ||
| type Phase string | ||
|
|
||
| const ( | ||
| // PhasePlan 规划阶段:构建上下文、调用 provider 直至得到 assistant 消息(含工具调用决策)。 | ||
| PhasePlan Phase = "plan" | ||
| // PhaseExecute 执行阶段:执行本批次全部工具调用。 | ||
| PhaseExecute Phase = "execute" | ||
| // PhaseVerify 验证阶段:工具结果已回灌,等待下一轮 provider 校验或收尾。 | ||
| PhaseVerify Phase = "verify" | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package controlplane | ||
|
|
||
| // ProgressEvidenceKind 标识工具/适配器产出的证据类型,runtime 仅聚合不做语义推断。 | ||
| type ProgressEvidenceKind string | ||
|
|
||
| const ( | ||
| // EvidenceNewInfoNonDup 表示本轮引入了非重复的新信息(用于 streak 回归约束)。 | ||
| EvidenceNewInfoNonDup ProgressEvidenceKind = "EVIDENCE_NEW_INFO_NON_DUP" | ||
| ) | ||
|
|
||
| // ProgressEvidenceRecord 描述一条可计分的进展证据。 | ||
| type ProgressEvidenceRecord struct { | ||
| Kind ProgressEvidenceKind `json:"kind"` | ||
| Detail string `json:"detail,omitempty"` | ||
| } | ||
|
|
||
| // ProgressScore 表示一次评估后的分值增量与 streak 快照。 | ||
| type ProgressScore struct { | ||
| ScoreDelta int `json:"score_delta"` | ||
| NoProgressStreak int `json:"no_progress_streak"` | ||
| RepeatCycleStreak int `json:"repeat_cycle_streak"` | ||
| } | ||
|
|
||
| // ProgressState 汇总当前运行期 progress 控制面状态。 | ||
| type ProgressState struct { | ||
| LastScore ProgressScore `json:"last_score"` | ||
| } | ||
|
|
||
| // ApplyProgressEvidence 根据证据更新分值与 streak。 | ||
| func ApplyProgressEvidence(state ProgressState, records []ProgressEvidenceRecord) ProgressState { | ||
| next := state.LastScore | ||
| if len(records) == 0 { | ||
| next.NoProgressStreak++ | ||
| } else { | ||
| next.NoProgressStreak = 0 | ||
| next.ScoreDelta++ | ||
| } | ||
| return ProgressState{LastScore: next} | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package controlplane | ||
|
|
||
| import "testing" | ||
|
|
||
| func TestApplyProgressEvidenceNoEvidenceIncrementsNoProgress(t *testing.T) { | ||
| t.Parallel() | ||
| state := ProgressState{} | ||
| next := ApplyProgressEvidence(state, nil) | ||
| if next.LastScore.NoProgressStreak != 1 { | ||
| t.Fatalf("expected no_progress_streak 1, got %d", next.LastScore.NoProgressStreak) | ||
| } | ||
| } | ||
|
|
||
| func TestApplyProgressEvidenceOnlyNonDupResetsNoProgressStreak(t *testing.T) { | ||
| t.Parallel() | ||
| state := ProgressState{ | ||
| LastScore: ProgressScore{NoProgressStreak: 3}, | ||
| } | ||
| next := ApplyProgressEvidence(state, []ProgressEvidenceRecord{ | ||
| {Kind: EvidenceNewInfoNonDup}, | ||
| }) | ||
| if next.LastScore.NoProgressStreak != 0 { | ||
| t.Fatalf("expected streak reset to 0, got %d", next.LastScore.NoProgressStreak) | ||
| } | ||
| if next.LastScore.ScoreDelta != 1 { | ||
| t.Fatalf("expected score_delta 1, got %d", next.LastScore.ScoreDelta) | ||
| } | ||
| } | ||
|
|
||
| func TestApplyProgressEvidenceMixedResetsNoProgress(t *testing.T) { | ||
| t.Parallel() | ||
| state := ProgressState{ | ||
| LastScore: ProgressScore{NoProgressStreak: 2}, | ||
| } | ||
| next := ApplyProgressEvidence(state, []ProgressEvidenceRecord{ | ||
| {Kind: EvidenceNewInfoNonDup}, | ||
| {Kind: ProgressEvidenceKind("other_evidence")}, | ||
| }) | ||
| if next.LastScore.NoProgressStreak != 0 { | ||
| t.Fatalf("expected streak reset, got %d", next.LastScore.NoProgressStreak) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package controlplane | ||
|
|
||
| // StopReason 表示一次 Run 的最终停止原因,互斥且由决议器唯一确定。 | ||
| type StopReason string | ||
|
|
||
| const ( | ||
| // StopReasonSuccess 表示助手正常结束(无待执行工具调用)。 | ||
| StopReasonSuccess StopReason = "success" | ||
| // StopReasonMaxLoops 表示达到配置的最大推理轮数。 | ||
| StopReasonMaxLoops StopReason = "max_loops" | ||
| // StopReasonError 表示不可恢复的运行时或 provider 错误。 | ||
| StopReasonError StopReason = "error" | ||
| // StopReasonCanceled 表示运行上下文被取消(含用户中断)。 | ||
| StopReasonCanceled StopReason = "canceled" | ||
| ) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.