-
Notifications
You must be signed in to change notification settings - Fork 565
feat(tools): add support for streaming command output in bash_tool.go #177
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
base: main
Are you sure you want to change the base?
feat(tools): add support for streaming command output in bash_tool.go #177
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @Vinay-Khanagavi for taking a crack at it,
I have some comments.
// Detect streaming commands (e.g., kubectl logs -f, tail -f) | ||
isStreaming := false | ||
for _, arg := range cmd.Args { | ||
if arg == "-f" || arg == "--follow" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect it will treat kubectl apply -f
a streaming command. Probably better to be very specific here and we can check for presense of ("kubectl logs" and "-f") or ("kubectl tail" and "-f") ...and so on...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For streaming detection, do you prefer I check specifically for commands like ( "kubectl logs -f") or ("kubectl tail -f") (i.e., both the base command and the -f flag), instead of just any command with -f?
Let me know if you have a preferred way to match these patterns in the codebase.
@@ -122,12 +125,86 @@ type ExecResult struct { | |||
} | |||
|
|||
func executeCommand(cmd *exec.Cmd) (*ExecResult, error) { | |||
// Detect streaming commands (e.g., kubectl logs -f, tail -f) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it will be good to add the original github issue that prompted this change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup! I’ll add a reference to the original GitHub issue (e.g., Closes #122) in the code comments,
Let me know if you have a preferred location for this reference (code comment vs. PR body).
|
||
if isStreaming { | ||
// Print streaming message | ||
fmt.Fprintln(os.Stdout, "\nStreaming output... Press CTRL-C to stop streaming and return to the prompt.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid writing to the os.Stdout
directly. Use the ui
instance to write to the screen. You will have to make ui
instance available from the caller to all the way here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing out the need to use the ui instance instead of writing directly to os.Stdout.
Do you have any suggestions or examples from the codebase on the best way to pass the ui instance down to executeCommand (and the goroutines), especially since it’s currently not available at this level?
Would you recommend passing it as a parameter, using context, or is there a preferred pattern in this project?
@Vinay-Khanagavi really swamped right now, I will get to this PR tomorrow, sorry for the delay. |
pkg/tools/bash_tool.go
Outdated
if isStreaming { | ||
// Print streaming message | ||
fmt.Fprintln(os.Stdout, "\nStreaming output... Press CTRL-C to stop streaming and return to the prompt.") | ||
|
||
stdoutPipe, err := cmd.StdoutPipe() | ||
if err != nil { | ||
results.Error = err.Error() | ||
return results, err | ||
} | ||
stderrPipe, err := cmd.StderrPipe() | ||
if err != nil { | ||
results.Error = err.Error() | ||
return results, err | ||
} | ||
|
||
if err := cmd.Start(); err != nil { | ||
results.Error = err.Error() | ||
return results, err | ||
} | ||
|
||
// Channel to catch interrupt | ||
interrupt := make(chan os.Signal, 1) | ||
signal.Notify(interrupt, os.Interrupt) | ||
defer signal.Stop(interrupt) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This Signal will also interrupt the current conversation
Running: kubectl logs echo-pod -f
Streaming output... Press CTRL-C to stop streaming and return to the prompt.
zvdy was here
zvdy was here
zvdy was here
zvdy was here
zvdy was here
zvdy was here
zvdy was here
zvdy was here
zvdy was here
zvdy was here
zvdy was here
zvdy was here
zvdy was here
zvdy was here
zvdy was here
zvdy was here
zvdy was here
zvdy was here
zvdy was here
^CReceived signal, shutting down... interrupt
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for raising this! @zvdy
I've updated the code to proactively block not just kubectl get ... -w but also other blocking/streaming commands like kubectl logs ... -f. Now, if the model tries to run a command with -w or -f in unattended mode, it will return a clear error message instead of running the command and blocking the session.
So, you won’t see the infinite streaming or need to manually interrupt anymore.
If you spot any other kubectl commands that can block like this, let me know and I’ll add them to the block list as well!
Let me know if you have any other suggestions or if you want to test further. Thanks for reviewing!
Thank you for the review. Please feel free to review and suggest any additional changes if needed. Thank you! |
Another more complex edge case would be Also, my idea was to allow the cli to stream the messages "infinitely" until user wants to stop and feed it back to the ai so it can work upon the data, analyse and take action. Take as an example a microservice that logs out that the service might experience high response times due to CPU/MEMORY/IO resource, the probes might be passing but due to bad scaling and overall configuration there is a bottleneck that can be fixed. This are more complex issues, and I'm assuming the verbosity of the pod in question, but having the model read that, will guide them into modifying the definition accordingly with the demand of the service. What do you think? @Vinay-Khanagavi @droot |
This could be a game-changer for Kubernetes troubleshooting and self-healing, but will require careful design for safety, cost, and user trust. Would love to hear your perspectives on scope, priorities, and potential pilot features! @zvdy @droot — would appreciate your thoughts! I've been thinking about this as well. My main concerns center on security, cost, and ensuring that users retain full control over any automated actions. I think this is a great direction for future exploration, maybe as an opt-in “advanced mode” or with clear user controls, nice thanks for mentioning.
|
Sorry @Vinay-Khanagavi , This PR has turned out to be more complex than I thought. Thanks @zvdy for finding the edge cases and input. This PR is challenging us into another area of agentic design (collab with a human and how to share control of interaction with the environment as well as I/O) -- when agent requires user's attention (blocking command), it needs to pass the control to the end-user for that action. (think browser based agent, that requires asking user to login etc.) I would like us to do the following:
Looking at the PR, I think we will have good handle on the technical aspect, but more work is needed on the audit and behavior definition. |
Adds support for streaming command output in bash_tool.go for commands like kubectl logs -f and tail -f.
Prints a message:
Streaming output... Press CTRL-C to stop streaming and return to the prompt.
This improves the UX for monitoring long-running or initializing workloads.
Closes #122