-
Notifications
You must be signed in to change notification settings - Fork 0
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
feat: add base agent #12
base: main
Are you sure you want to change the base?
Conversation
609d2d0
to
00c221f
Compare
Hey @sameo 👋, we did some cleanup on our branch. You might notice some things:
|
00c221f
to
1574ed5
Compare
I merged #9 now. For me to review this PR, I need you to:
|
9aff95a
to
658d128
Compare
Hello @sameo We both have Rebase it on top of main and clean our git history up without the The PR is ready to be reviewed 😄 |
1a0fcaf
to
09220bf
Compare
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 PR is made of 14 commits, that show you put a lot of work and time on it. As a teacher, I appreciate that and I can see there's been a lot of fixups, feature additions, removal, files deleted and removed, etc.
Future users of your code will not care at all about it though. They may want to understand what you were trying to do, and you have the opportunity to tell them a nice and logical story about your work. Something like:
- Hey, I am going to implement a serverless agent, running on a guest and talking to a host. Here is my AP describing the requests, responses and status messages of my protocol. That my first commit.
- When I receive a request from the host, I will run a serverless workload, step by step. I will use a workspace for that where I will run the commands I got from the host request, and sent the output of those commands back to the host. This is my second commit.
- The agent can be configured through a config file. I'm describing it on my 3rd commit.
- Now that we have all the pieces together, I can actually implement my main function. It uses all the APIs I defined in my first 3 commits to implement the main lambdo agent flow.
Please tell me that kind of story.
agent/lib/src/external_api/model.rs
Outdated
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] | ||
pub enum Type { | ||
Status, | ||
Request, | ||
Response, | ||
} | ||
|
||
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] | ||
pub enum Code { | ||
Run, | ||
Ok, | ||
} |
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 model could be improved in many ways, but basically a more scalable and efficient one would look like:
use serde::{Deserialize, Serialize};
#[repr(u16)]
pub enum MessageType {
Status,
Request,
Response,
}
#[derive(Deserialize, Serialize, Debug)]
pub enum StatusCode {
Ok,
Failed,
Foo,
Bar,
}
#[derive(Deserialize, Serialize, Debug)]
pub struct Step {
}
#[derive(Deserialize, Serialize, Debug)]
pub struct File {
}
#[derive(Deserialize, Serialize, Debug)]
pub enum MessagePayload {
Status { status: StatusCode },
Request { id: String, files: Vec<File>, steps: Vec<Step>},
// Response,
}
pub struct MessageHeader {
r#type: MessageType,
length: u16,
}
pub struct AgentMessage {
header: MessageHeader,
payload: MessagePayload,
}
Then:
let mut header = [0u16; 2];
serialport.read(port, &mut header);
let message_type = header[0];
let message_length = header[1];
// Read message_length bytes from the serial port
// Depending on message_type, deserialize into the correct payload
With that approach, you have one single type of message where the payload differs. And the header is short and describes the message type. You can then read anything from your serial port, and not assume you're only going to receive one kind of message.
I am not asking you to move to that model, because we're only a few days away from the deadline. But please address my other comments.
agent/lib/src/external_api/model.rs
Outdated
pub struct FileModel { | ||
pub filename: String, | ||
pub content: String, | ||
} | ||
|
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.
What is a FileModel
for?
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.
FileModel
Represents the files that the API sends us as defined in this message.
I'll document this
agent/lib/src/external_api/model.rs
Outdated
pub files: Vec<FileModel>, | ||
pub steps: Vec<RequestStep>, | ||
} |
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.
How do the steps relate to the files?
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.
The steps
field in the api's requests list all commands to run in order to execute the code. For instance in a NodeJS Project we would have a npm ci
to install dependencies, followed by a node dist/index.js
, in this example the steps relates to the files because a package.json
is needed to install the dependencies.
agent/lib/src/external_api/model.rs
Outdated
@@ -0,0 +1,139 @@ | |||
use serde::{Deserialize, Serialize}; |
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.
On the name of that file: There's only one API, and that's the one between the agent and the host. So please rename to api/model.rs
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 is not addressed yet.
// Flush the serial port | ||
self.serial_port | ||
.flush() | ||
.map_err(|e| anyhow!("Failed to flush serial port: {}", e))?; |
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.
What do we do with the flushed bytes? Maybe there were some pending bytes from the host?
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 didn't think about that. I'm deleting this
.write_all(buf) | ||
.map_err(|e| anyhow!("Failed to write to serial port: {}", e))?; | ||
|
||
// In order to still be readable by ``readline`` on the other side, we add a carriage return |
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.
If the other side
decides to use readline
to read the agent's output, it's none of the agent's business. You should not add unspecified data to your protocol.
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.
Normally I would absolutely agree with you.
However since the API uses BufReader::read_line they need a carriage return, else the API just blocks meaning that we have to send it.
I know this looks ridicoulous, I tried to tell them to stick to what we have defined.
fn random_usize(max: usize) -> usize { | ||
let mut f = File::open("/dev/urandom").unwrap(); | ||
let mut buf = [0u8; 1]; | ||
f.read_exact(&mut buf).unwrap(); | ||
let value = buf[0] as usize; | ||
|
||
if value < max { | ||
max | ||
} else { | ||
value % max | ||
} | ||
} | ||
|
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.
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 didnt use it as I wasn't aware of the [dev-dependencies]
section. Replaced 👍
agent/lib/src/external_api/model.rs
Outdated
pub struct CodeEntry { | ||
pub files: Vec<FileModel>, | ||
pub script: Vec<String>, // All commands to execute at startup | ||
} |
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.
Is CodeEntry
the entry point for running some piece of code?
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 is a legacy piece of code that is not used anymore, replaced by RequestData
. Thank you for noticing it
agent/lib/src/external_api/model.rs
Outdated
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] | ||
pub struct FileModel { | ||
pub filename: String, | ||
pub content: String, |
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.
Is content
the content of the file at filename
? Is filename
a path? or just a name?
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 is a path relative to the workspace directory in internal::service
09220bf
to
79bab13
Compare
Here are the fixes, I hope these are okay |
Please rebase this PR on top of main. Do not try to merge main back into it. Let me know if you need help with this. |
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.
As explained, do not merge main into this PR, rebase on top of it instead.
Also, the lumper submodule update is confusing, to say the least.
And on this PR commits:
- 79bab13 is a bag of random fixes. Please have each of them merged into the commit it's fixing.
- Describe what the commit is doing. For example, commit f322737 says:
feat: basic structures, code exec and RW
* feat: define structures
* feat: add read & write from serial method
* feat: create workspace method internal api
* test(external_api): parse json and check delimiter
* feat: read both stderr and stdout
* chore: testing InternalApi.create_workspace()
But does not tell me anything about what this commit is doing and how it's doing it.
agent/lib/src/external_api/model.rs
Outdated
@@ -0,0 +1,139 @@ | |||
use serde::{Deserialize, Serialize}; |
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 is not addressed yet.
} | ||
|
||
/// Serializes an Option<String> as a String by returning an empty string if the Option is None | ||
fn serialize_optionnal_string<S>(value: &Option<String>, serializer: S) -> Result<S::Ok, S::Error> |
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.
nit:
fn serialize_optionnal_string<S>(value: &Option<String>, serializer: S) -> Result<S::Ok, S::Error> | |
fn serialize_optional_string<S>(value: &Option<String>, serializer: S) -> Result<S::Ok, S::Error> |
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 still needs to be fixed.
agent/lib/src/api/model.rs
Outdated
result: i32, | ||
stdout: Option<String>, | ||
stderr: String, | ||
enable_output: bool, |
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.
You should not need that if stdout
is an Option
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 am not sure to understand what you mean 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.
You know if output is enabled or not by looking at stdout
:
if let Some(r.stdout) {
// output is enabled
} else {
// output is disabled
}
So you no longer need the enable_output
field.
agent/lib/src/api/model.rs
Outdated
pub enum Code { | ||
/// Represents a request to run the code or a response to such request | ||
Run, | ||
/// Indicates that your status is that you are ok and ready to communicate |
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.
/// Indicates that your status is that you are ok and ready to communicate | |
/// Agent is ready to communicate. |
Please rename it to Ready
at least.
agent/lib/src/api/model.rs
Outdated
/// Identifies the type of the message | ||
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] | ||
pub enum Type { | ||
/// Status message to indicate that the agent is ready | ||
Status, | ||
/// Request message | ||
Request, | ||
/// Response message answering to a request message | ||
Response, | ||
} |
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 don't think Type
is checked or used anywhere, except for decorating your messages with that information. As explained in #12 (comment), you will have to create a new structure (as opposed to extending a payload enum) for any new message that you'd want to extend your protocol with. So you don't need this Type
enum and you should get rid of it.
agent/lib/src/api/model.rs
Outdated
/// Command that was run | ||
pub command: String, | ||
/// Exit code of the command | ||
pub result: i32, |
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.
pub result: i32, | |
pub exit_code: i32, |
agent/lib/src/api/model.rs
Outdated
pub stderr: String, | ||
/// Whether the stdout should be returned or not | ||
#[serde(alias = "enableOutput")] | ||
pub enable_output: bool, |
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.
Again, I dont think this is needed.
@@ -0,0 +1,116 @@ | |||
use anyhow::Result; |
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.
lumper
is a submodule. Where are those files coming from?
1ab2274
to
2de3333
Compare
Signed-off-by: iverly <[email protected]>
Signed-off-by: iverly <[email protected]>
Signed-off-by: iverly <[email protected]>
Signed-off-by: iverly <[email protected]>
2de3333
to
2ee2be9
Compare
Signed-off-by: GridexX <[email protected]>
Signed-off-by: GridexX <[email protected]>
Signed-off-by: GridexX <[email protected]>
2ee2be9
to
6fa890b
Compare
@sameo @WoodenMaiden |
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.
A couple nits and we should be able to merge that one.
The commit messages are still empty, fwiw.
} | ||
|
||
/// Serializes an Option<String> as a String by returning an empty string if the Option is None | ||
fn serialize_optionnal_string<S>(value: &Option<String>, serializer: S) -> Result<S::Ok, S::Error> |
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 still needs to be fixed.
@@ -14,8 +14,12 @@ serde = { version = "1.0.152", features = ["derive"] } | |||
serde_json = "1.0.93" | |||
serde_yaml = "0.9" | |||
clap = { version="4.1.6", features=["derive"] } | |||
unshare = "0.7.0" |
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.
Where is this crate used?
Hello!
This PR aims to add the agent, whose job is:
Resolves #5 & #6