Skip to content
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

Langchain integration #512

Merged
merged 25 commits into from
Jul 23, 2023
Merged

Conversation

UmerHA
Copy link
Collaborator

@UmerHA UmerHA commented Jul 7, 2023

Strongly based on #466 - shoutout to @hrobbins!


What it does:
Enables using any model that's available in LangChain as backend.

How to use it:

  • For gpt4/3.5 it's the same usage, i.e. gpt-engineer <path_to_project> gpt-4
  • For other models, you have to provide a <model_name>.yaml file with the model configs. Then gpt-engineer <path_to_project> <model_name> --modeldir <path_to_model_config_file>

How it works:

  • gpt-engineer search for a <model_name>.yaml and passes its content to LangChain, which then creates the model.
  • per default the <model_name>.yaml is searched for in the package folder /models.
  • ai.py has been adapted to use LangChain

In the future, more model configs can be added, so more models are available by default.

@UmerHA UmerHA marked this pull request as draft July 7, 2023 19:19
@UmerHA
Copy link
Collaborator Author

UmerHA commented Jul 7, 2023

need to fix typing issues

@UmerHA
Copy link
Collaborator Author

UmerHA commented Jul 7, 2023

Hey, the failing tests don't have anything to do with the changed code, so I'd rather not fix them here.

Test 1: test_collect.py/test_collect_learnings:
Issue is that json.dumps([{"role": "system", "content": code}]) turns the \n into \\n.
Reproducible via

	import json
	code = "this is output\n\nit contains code"
	json.dumps([{"role": "system", "content": code}])

Test 2: test_db.py/test_error_messages:
Issue lies in DB class, which hasn't been touched in this PR.

@UmerHA UmerHA marked this pull request as ready for review July 7, 2023 23:13

# initialize token usage log
self.cumulative_prompt_tokens = 0
self.cumulative_completion_tokens = 0
self.cumulative_total_tokens = 0
self.token_usage_log = []

# ToDo: Adapt to arbitrary model, currently assumes model using tiktoken
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
# ToDo: Adapt to arbitrary model, currently assumes model using tiktoken

Choose a reason for hiding this comment

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

+1 on this.
having a hard requirement on tiktoken could reduce the usability of this feature.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Unfortunately, LangChain doesn't give access to its models' tokenizers. So we would either (i) wait until LC does so, or (ii) implement each tokenizer for each model into gpt-engineer. Imo (ii) doesn't make sense because we want to let LC handle all the interactions with models, and focus on coding-specific stuff.

def deserialize_messages(jsondictstr: str) -> List[Message]:
return AI.parse_langchain_basemessages(
messages_from_dict(json.loads(jsondictstr))
)
Copy link
Owner

Choose a reason for hiding this comment

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

Can we just do:

return list(messages_from_dict(json.loads(jsondictstr)) ?

Copy link
Owner

Choose a reason for hiding this comment

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

And delete below function

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Unfortunately no. Doing list(...) gives a List[BaseMessage], but we need a List[Message] (which is defined as List[Union[AIMessage, HumanMessage, SystemMessage]]).

The complexity comes from the fact that List is invariant, meaning List[Some_Subclass] is not accepted where List[Some_Class] is expected.

For context:

  • BaseMessage is a LangChain class and the superclass of the LC classes AIMessage, HumanMessage, and SystemMessage
  • Message is defined in this PR and is an alias for Union[AIMessage, HumanMessage, SystemMessage]

We have to choose between one of these options:

  1. Don't do typing at all
  2. Use same type as LC: Use Sequence[BaseMessage] in gpt-engineer. This works because Sequence unlike List is covariant. But Sequence is immutable. So everywhere we add to the messages, we would have to convert to List, do the operation, then re-convert to Sequence. Imo this makes the code complex and repetitive.
  3. Parse at the point of interaction with LC: Parse between LC's Sequence[BaseMessage] and our List[Message] only at the LC interface. Then we can keep our code simple.

I think 3 is cleanest, so I chose that.

What are your thoughts?

Copy link
Owner

@AntonOsika AntonOsika Jul 12, 2023

Choose a reason for hiding this comment

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

Right the typing. My thinking is that we use typing.cast() (and assert isinstance(...) if necessary)?

Otherwise the code is very confusing to the reader and I'd even prefer doing 1!
(which is a legit option: just adding # type: ignore

gpt_engineer/ai.py Outdated Show resolved Hide resolved
gpt_engineer/ai.py Outdated Show resolved Hide resolved
models/gpt-4.yaml Outdated Show resolved Hide resolved
gpt_engineer/ai.py Outdated Show resolved Hide resolved
def last_message_content(self, messages: List[Message]) -> str:
m = messages[-1].content.strip()
logging.info(m)
return m
Copy link
Owner

Choose a reason for hiding this comment

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

I think this function makes the code more difficult to read for a human.

Would prefer to remove it!

And logging.info(m) will be confusing no?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

  1. Agree & deleted logging.info(m)
  2. Disagree with last_message_content making the code more difficult to read. We're using that function 11 times (see image), so think makes sense to not always rewrite it, and the function name is pretty self-explaining IMO. But will do what you decide. Lmk!

image

Copy link
Owner

Choose a reason for hiding this comment

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

Great on 1.

On the other, it's just that it's:

  1. More characters to type
  2. Needs an external dependency (AI)
  3. which is an object? (even though it doesn't need to be initialized)
  4. hides away unnecessarily, how simple everything actually is: It's just a list of messages! (knowing this makes it easier to understand the code)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Okay, get your point, will change!

@AntonOsika
Copy link
Owner

AntonOsika commented Jul 8, 2023

Good job @UmerHA! Soon there 🏃

The big concern from my side is the "model_dir" and "model_name" and that gpt4 is not supported.

Actualy, it's not on our roadmap to add open source models.

Also, I'd like to keep the code as simple as possible, and I'm not sure it is worth it for anyone to use local models? GPT4 is much better.

I'd propose we remove that part for now and potentially bring the local model stuff in a separate PR.

Thoughts?

@hrobbins
Copy link
Contributor

hrobbins commented Jul 8, 2023 via email

@hrobbins
Copy link
Contributor

hrobbins commented Jul 9, 2023 via email

@UmerHA
Copy link
Collaborator Author

UmerHA commented Jul 9, 2023

@AntonOsika Taking a step back: What's the reason for integrating with LangChain if we don't want other models?
My understanding was that's why we're doing #466, on which this PR is based.

Apart from that I agree that gpt4 is currently the best model, but I would expect that non-openai models (incl. open-source) get more capable over time, so I would vote to enable them.

Also, is there a roadmap I can consult? Of course don't want to build stuff not on the roadmap. :)

@AntonOsika
Copy link
Owner

Hey cool didn’t know there was a gpt4 model accessible in this way? Does it work out of the box with this approach.

As long as it didn’t interfere with current way of using its great, happy to merge after some other review comments are addressed. it’s just that the way of putting messages together seems like it shouldn’t be supported by gpt4?

also — thanks both for contributions on this

@hrobbins
Copy link
Contributor

hrobbins commented Jul 10, 2023 via email

@hrobbins
Copy link
Contributor

hrobbins commented Jul 11, 2023 via email

- deleted unnecessary functions & logger.info
- used LangChain ChatLLM instead of LLM to naturally communicate with gpt-4
- deleted loading model from yaml file, as LC doesn't offer this for ChatModels
@UmerHA
Copy link
Collaborator Author

UmerHA commented Jul 11, 2023

Hey @AntonOsika, I incorporated your feedback.

I also removed the model_dir & gpt4/3.5.yaml files, because LangChain doesn't offer loading from config files for ChatModels yet.

gpt_engineer/steps.py Outdated Show resolved Hide resolved
return "\n".join(
[f"{message['role']}:\n\n{message['content']}" for message in messages]
)

Copy link
Owner

Choose a reason for hiding this comment

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

This breakes tests. See CI.

Any reason of changing it?

Copy link
Collaborator Author

@UmerHA UmerHA Jul 14, 2023

Choose a reason for hiding this comment

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

Deleting format_messages is not the issue. That function is not used anywhere. That's why I deleted it.

The test test_collect_learnings breaks because in the test we're doing sth like

code = "this is output\n\nit contains code"
code_with_extra_stuff = "extra stuff" + json.dumps(code)
assert code in code_with_extra_stuff

but json.dumps escapes the \n and turns it into \\n. That's one of the failing tests mentioned above which iiuc don't relate to the changes in this PR.

I will fix the tests by changing assert code in code_with_extra_stuff to assert json.dumps(code) in code_with_extra_stuff

@AntonOsika
Copy link
Owner

Cool! 🏃

To be honest I think AI abstraction is a bit too complex right now and would like a proposal of a refactor. But I think it's OK we merge after we address current comments.

As @hrobbins points out I think ChatMessageHistory would be great.

UmerHA added 2 commits July 14, 2023 06:42
- Fixed failing test
- Removed parsing complexity by using # type: ignore
- Replace every ocurence of ai.last_message_content with its content
@AntonOsika
Copy link
Owner

Let me know when you want me to take a look! 🙏

@UmerHA
Copy link
Collaborator Author

UmerHA commented Jul 18, 2023

Let me know when you want me to take a look! 🙏

@AntonOsika You can take a look :)
(Sry for being absent the last few days - took time off to celebrate my birthday 😃)

gpt_engineer/steps.py Outdated Show resolved Hide resolved
@AntonOsika
Copy link
Owner

Happy birthday @UmerHA
And great work! 🚀

Same with me – very happy to get on top of PRs and see this.

Let's merge 🏃

@AntonOsika AntonOsika merged commit 19a4c10 into AntonOsika:main Jul 23, 2023
@UmerHA UmerHA deleted the langchain-integration branch August 16, 2023 10:45
70ziko pushed a commit to 70ziko/gpt-engineer that referenced this pull request Oct 25, 2023
* Added LangChain integration

* Fixed issue created by git checkin process

* Added ':' to characters to remove from end of file path

* Tested initial migration to LangChain, removed comments and logging used for debugging

* Tested initial migration to LangChain, removed comments and logging used for debugging

* Converted camelCase to snake_case

* Turns out we need the exception handling

* Testing Hugging Face Integrations via LangChain

* Added LangChain loadable models

* Renames "qa" prompt to "clarify", since it's used in the "clarify" step, asking for clarification

* Fixed loading model yaml files

* Fixed streaming

* Added modeldir cli option

* Fixed typing

* Fixed interaction with token logging

* Fix spelling + dependency issues + typing

* Fix spelling + tests

* Removed unneeded logging which caused test to fail

* Cleaned up code

* Incorporated feedback

- deleted unnecessary functions & logger.info
- used LangChain ChatLLM instead of LLM to naturally communicate with gpt-4
- deleted loading model from yaml file, as LC doesn't offer this for ChatModels

* Update gpt_engineer/steps.py

Co-authored-by: Anton Osika <[email protected]>

* Incorporated feedback

- Fixed failing test
- Removed parsing complexity by using # type: ignore
- Replace every ocurence of ai.last_message_content with its content

* Fixed test

* Update gpt_engineer/steps.py

---------

Co-authored-by: H <[email protected]>
Co-authored-by: Anton Osika <[email protected]>
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.

5 participants