Skip to content

Update Pregel files to match Python implementation: debug.ts, io.ts, read.ts, types.ts, write.ts#120

Merged
Nuno Campos (nfcampos) merged 20 commits intolangchain-ai:mainfrom
andrewnguonly:migrate-pregel
Apr 30, 2024
Merged

Update Pregel files to match Python implementation: debug.ts, io.ts, read.ts, types.ts, write.ts#120
Nuno Campos (nfcampos) merged 20 commits intolangchain-ai:mainfrom
andrewnguonly:migrate-pregel

Conversation

@andrewnguonly
Copy link
Copy Markdown
Contributor

@andrewnguonly Andrew Nguonly (andrewnguonly) commented Apr 24, 2024

Summary

The following files are updated (as much as possible) to match the corresponding Python implementation: debug.ts, io.ts, read.ts, types.ts, write.ts. The goal is to update the files without breaking existing tests or adding new functionality that would require significant changes to the Pregel class.

The implementation does not match exactly 1:1 with the Python implementation. I'll leave comments directly in the PR to note any differences.

Implementation

  1. debug.ts: Small updates.
  2. io.ts: Moved readChannel() function to this file. Added the following unused functions readChannels(), mapOutputValues(), and mapOutputUpdates().
  3. read.ts: Renamed ChannelInvoke to PregelNode. Added more fields to PregelNode. Remove PregelNode.when. Update PregelNode.pipe() to support checking ChannelWrite.
  4. types.ts: New file created to match Python implementation.
  5. write.ts: Add ChannelWrite.isWriter() and ChannelWrite.registerWriter().
  6. Add RunnableCallable class as part of fixing bug in Fix bug where saving a runnable or function in state object would misbehave langgraph#345. I haven't migrated all instances of RunnableLambda to RunnableCallable yet. Will do this in a subsequent PR.

To Do

Next Steps

  1. Update pregel/index.ts (Pregel and Channel classes).
  2. Migrate RunnableLambda to RunnableCallable.
  3. Update pregel/validate.ts (I think).

input: any;
proc: Runnable;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
writes: Array<[string, any]>; // TODO: Array type may need to be changed
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Python implementation uses collections.deque and the implementation of Pregel depends on the deque APIs. I need to figure out the appropriate type later.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we use deque in python for thread safety. in js fine to use array

/**
* Nodes to execute in the next step, if any
*/
next: [string];
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double check: This needs to be Array<string>.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes current type is wrong

default: null,
annotation: "TYPE_SEND",
isShared: true,
isShared: false,
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

static registerWriter(runnable: Runnable): ChannelWrite {
return runnable as ChannelWrite;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double check: Not used at the moment, but need to double check that this is the desired implementation.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not, but you can fix it later


static isWriter(runnable: RunnableLike): boolean {
// eslint-disable-next-line no-instanceof/no-instanceof
return runnable instanceof ChannelWrite;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double check: Currently used in PregelNode.pipe(). Need to double check that this is the desired implementation.

}
}

function _readChannel(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is moved to io.ts.

if (proc.when === undefined || proc.when(val)) {
tasks.push([proc, val, name]);
}
tasks.push([proc, val, name]);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if this is valid, but all of the existing tests are passing.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why would it not be valid?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just comparing with the current Python implementation which relies on the for_execution API, which isn't implemented yet in JS.

https://github.com/langchain-ai/langgraph/blob/main/langgraph/pregel/__init__.py#L1287

lc_graph_name = "ChannelInvoke";
lc_graph_name = "PregelNode";

channels: Record<string, string> | string;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

channels is supposed to be type Record<string, string> | string[], but I couldn't make this change without breaking a significant amount of tests. I'll make this update when I update the Pregel class.

writers?: Runnable[];
tags?: string[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
bound?: Runnable<any, any>;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be Runnable<RunInput, RunOutput>?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

when?: (args: any) => boolean;
config?: RunnableConfig;
mapper?: (args: any) => any;
writers?: Runnable[];
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be Runnable<RunInput, RunOutput>[]?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one should be Runnable<RunOutput, RunOutput>[]

triggers: string[] = [];

// eslint-disable-next-line @typescript-eslint/no-explicit-any
when?: (args: any) => boolean;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed all instances and usages of when. I think this was a deprecated feature?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep this not used anymore

triggers: this.triggers,
mapper: this.mapper,
writers: [...this.writers, coerceable as ChannelWrite],
bound: this.bound.pipe(coerceable),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This diverges from the Python implementation. I couldn't get the existing tests to pass without piping coerceable. Any advice?

Python implementation: https://github.com/langchain-ai/langgraph/blob/main/langgraph/pregel/read.py#L201

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's because you need other changes in Pregel class I think

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it ok to leave this change in for now?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, but make a note to change this

): Generator<[string, any]> {
if (typeof inputChannels === "string") {
yield [inputChannels, chunk];
if (chunk) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.join(",")}>`;
super({
...{ channels, name },
...{ channels: writes, name },
Copy link
Copy Markdown
Contributor

@nfcampos Nuno Campos (nfcampos) Apr 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should bewrites in super call too?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unexpected change by VSCode when applying the Rename Symbol action. I changed to ...{ writes, name } and everything seems to work the same.

Copy link
Copy Markdown
Contributor

@nfcampos Nuno Campos (nfcampos) left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mostly lgtm, some minor comments


const valuesAwaited = await Promise.all(values);

async _getWriteValues(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nuno Campos (@nfcampos), the implementation of _getWriteValues() (and _write()) matches the Python implementation as specified in langchain-ai/langgraph#345. However, I only made the bare minimum changes in Pregel and StateGraph to make the existing tests pass.

I'll add a new test to match this one: https://github.com/langchain-ai/langgraph/pull/345/files#diff-9f99beb90b66284c77ac257b4c0dfc06dcb998302f7c6740d84759460f704f53

});

// TODO: fix this test
xit("can invoke a nested graph", async () => {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nuno Campos (@nfcampos), I copied this test, but I haven't figured out where the fix needs to be in the existing implementation (which will probably be changed later). Ideally, I'd like to get this fixed/working before merging. I'll ask you for help in the morning.

@nfcampos Nuno Campos (nfcampos) merged commit 099fafa into langchain-ai:main Apr 30, 2024
@andrewnguonly Andrew Nguonly (andrewnguonly) deleted the migrate-pregel branch April 30, 2024 20:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants