Skip to content

Commit d13c1cb

Browse files
authored
Merge pull request #127 from mindflowai/stream
Stream responses from GPT and lazy import CLI commands to improve performance.
2 parents 84b6114 + 62a98e0 commit d13c1cb

File tree

26 files changed

+435
-355
lines changed

26 files changed

+435
-355
lines changed

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ The [ChatGPT](https://openai.com/blog/chatgpt)-powered swiss army knife for the
2020
## Getting Started
2121

2222
Pre-requisite:
23-
- You'll need to create an [OpenAI](https://openai.com/blog/openai-api) account or request early access from [Anthropic](https://www.anthropic.com/earlyaccess).
23+
- You'll need to create an [OpenAI](https://openai.com/blog/openai-api) account.
2424
- Also, create a [Pinecone](https://www.pinecone.io/start) account to use their vector database.
2525

2626
1. Run `pip install mindflow`, or you can clone this repo and run `pip install -e path/to/mindflow`.
2727
2. Run `mf login`:
28-
- Register with OpenAI or Anthropic to use their models. You can find your OpenAI API key [here](https://platform.openai.com/account/api-keys).
28+
- Register with OpenAI to use their models. You can find your OpenAI API key [here](https://platform.openai.com/account/api-keys).
2929
- Register with Pinecone to use their vector database. You can find your Pinecone API key and Environment [here](https://www.pinecone.io/start).
3030
3. Now, you're ready to start using MindFlow!
3131

@@ -42,14 +42,14 @@ There are multiple levels to using mindflow's chat feature.
4242
- `mf chat "explain what a programming language is"`
4343
- Interact with chatGPT directly just like on the chatGPT website. We also have chat persistence, so it will remember the previous chat messages.
4444
2. With File Context
45-
- `mf chat "please summarize what this code does" path/to/code.py`
45+
- `mf chat path/to/code.py "please summarize what this code does"`
4646
- You can provide single or multi-file context to chatGPT by passing in any number of files as a separate argument in the `mf chat` call. For sufficiently small files (see: [chatGPT token limits](https://help.openai.com/en/articles/4936856-what-are-tokens-and-how-to-count-them)), this will work and also maintain chat history.
4747
3. With Directory Context
48-
- `mf chat "what are these submodules responsible for? path/to/submodule1/ path/to/submodule2/`
48+
- `mf chat path/to/submodule1/ path/to/submodule2/ "what are these submodules responsible for?"`
4949
- Providing directories will actually run an indexer over your code subdirectories/files recursively. So it may take a while to fully index everything -- don't worry; we'll warn you if the cost becomes a concern! Right now the warning triggers if the index job costs >$0.50USD.
5050
4. Custom pre-indexed context
5151
- `mf index path/to/subdir/file1.txt path/to/file2.txt`
52-
- `mf chat -s "How do all of my classes relate to one another?" ./`
52+
- `mf chat -s ./ "How do all of my classes relate to one another?"`
5353
- If you pre-index your repository, you can narrow the scope for the context provided to the chat. Passing `-s` will skip the auto-indexing, and instead will defer to the currently existing index. This index is generated in the first step `mf index` where only those files/subdirs will be included.
5454
- This can save you time and money if your repository is significantly large.
5555

@@ -118,7 +118,7 @@ Make some changes to your branch and stage, and then commit them. Then, run `mf
118118
![Screenshot 2023-03-11 at 8 42 11 PM](https://user-images.githubusercontent.com/26421036/224524839-45093b5d-b4d9-4dc4-a129-867d819a2136.png)
119119

120120
## How does it work?
121-
This tool allows you to build an index of text documents and search through them using GPT-based embeddings. The tool takes document paths as input, extracts the text, splits the documents into chunks, summarizes them, and builds a summarization tree. The tool then uses this tree to generate embeddings of the indexed documents and your query and selects the top text chunks based on the cosine similarity between these embeddings. The generated index can be saved to a JSON file for later reuse, making subsequent searches faster and cheaper.
121+
MindFlow uses state-of-the-art methods for high-throughput segmentation, processing, storage, and retrieval of documents using a recursive hierarchical summarization and embedding technique to store embedding vectors for document chunks and then achieve fast, and high-quality responses to questions and tasks by appending similar document chunks based on the hierarchically embedded text and using them as context for you query. Additionally, chat history will persist if it can fit in the context for queries over indexed documents or for regular chat.
122122

123123
## What's next for MindFlow
124124
In the future, MindFlow plans on becoming an even more integral part of the modern developer's toolkit. We plan on adding the ability to ditch traditional documentation and instead integrate directly with your private documents and communication channels, allowing for a more seamless and intuitive experience. With MindFlow, you can have a true "stream of consciousness" with your code, documentation, and communication channels, making it easier than ever to stay on top of your projects and collaborate with your team. We are excited to continue pushing the boundaries of what's possible with language models and revolutionizing how developers work.

mindflow/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.5.3"
1+
__version__ = "0.5.4"

mindflow/cli/commands/chat.py

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,10 @@
1-
import os
21
import click
3-
import asyncio
4-
52
from typing import Tuple
6-
from result import Result
7-
8-
from mindflow.core.commands.chat import run_chat
9-
from mindflow.core.commands.index import run_index
10-
from mindflow.core.commands.query import run_query
11-
from mindflow.core.settings import Settings
12-
from mindflow.core.types.model import ModelApiCallError
13-
from mindflow.core.types.store_traits.json import save_json_store
14-
from mindflow.core.types.conversation import Conversation
15-
from mindflow.core.types.definitions.conversation import ConversationID
163

174

185
def parse_chat_prompt_and_paths_from_args(prompt_args: Tuple[str]):
6+
import os
7+
198
prompt = " ".join(prompt_args) # include files/directories in prompt
209
paths = []
2110

@@ -32,6 +21,34 @@ def parse_chat_prompt_and_paths_from_args(prompt_args: Tuple[str]):
3221
@click.option("-s", "--skip-index", type=bool, default=False, is_flag=True)
3322
@click.argument("prompt_args", nargs=-1, type=str, required=True)
3423
def chat(prompt_args: Tuple[str], skip_index: bool):
24+
import click
25+
import asyncio
26+
27+
from typing import List
28+
from result import Ok
29+
30+
from mindflow.core.commands.chat import run_chat
31+
from mindflow.core.commands.index import run_index
32+
from mindflow.core.commands.query import run_query
33+
from mindflow.core.settings import Settings
34+
from mindflow.core.types.store_traits.json import save_json_store
35+
36+
async def stream_chat(settings: Settings, prompt: str):
37+
print("\nGPT:")
38+
async for char_stream_chunk in run_chat(settings, [], prompt):
39+
if isinstance(char_stream_chunk, Ok):
40+
click.echo(char_stream_chunk.value, nl=False)
41+
else:
42+
click.echo(char_stream_chunk.value)
43+
44+
async def stream_query(settings: Settings, file_paths: List[str], prompt: str):
45+
print("\nGPT:")
46+
async for char_stream_chunk in run_query(settings, file_paths, prompt):
47+
if isinstance(char_stream_chunk, Ok):
48+
click.echo(char_stream_chunk.value, nl=False)
49+
else:
50+
click.echo(char_stream_chunk.value)
51+
3552
prompt, paths = parse_chat_prompt_and_paths_from_args(prompt_args)
3653
settings = Settings()
3754
if paths:
@@ -46,18 +63,13 @@ def chat(prompt_args: Tuple[str], skip_index: bool):
4663

4764
asyncio.run(run_index(settings, paths))
4865

49-
run_query_result: Result[str, ModelApiCallError] = asyncio.run(
50-
run_query(settings, paths, prompt)
51-
)
52-
click.echo(run_query_result.value)
66+
asyncio.run(stream_query(settings, paths, prompt))
5367

5468
save_json_store()
5569
return
5670

57-
run_chat_result: Result[str, ModelApiCallError] = asyncio.run(
58-
run_chat(settings, [], prompt)
59-
)
60-
click.echo(run_chat_result.value)
71+
asyncio.run(stream_chat(settings, prompt))
72+
6173
save_json_store()
6274

6375

@@ -68,6 +80,9 @@ def history():
6880

6981
@history.command(help="View chat history stats.")
7082
def stats():
83+
from mindflow.core.types.conversation import Conversation
84+
from mindflow.core.types.definitions.conversation import ConversationID
85+
7186
if (conversation := Conversation.load(ConversationID.CHAT_0.value)) is None:
7287
print("No conversation history found.")
7388
return
@@ -78,6 +93,9 @@ def stats():
7893

7994
@history.command(help="Clear the chat history.")
8095
def clear():
96+
from mindflow.core.types.conversation import Conversation
97+
from mindflow.core.types.definitions.conversation import ConversationID
98+
8199
if (conversation := Conversation.load(ConversationID.CHAT_0.value)) is None:
82100
print("No conversation history found.")
83101
return

mindflow/cli/commands/config.py

Lines changed: 112 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,115 @@
1-
import sys
21
import click
32
from typing import List
43

5-
from mindflow.core.types.store_traits.json import save_json_store
6-
from mindflow.core.types.mindflow_model import (
7-
MindFlowModel,
8-
MindFlowModelConfig,
9-
MindFlowModelID,
10-
)
11-
12-
from mindflow.core.types.definitions.model import (
13-
ModelID,
14-
)
15-
from mindflow.core.types.model import Model
16-
174

185
@click.command(
196
help="Configure MindFlow. For example, you can configure the model to use."
207
)
218
def config():
9+
from mindflow.core.types.store_traits.json import save_json_store
10+
from mindflow.core.types.mindflow_model import (
11+
MindFlowModel,
12+
MindFlowModelConfig,
13+
MindFlowModelID,
14+
)
15+
16+
from mindflow.core.types.definitions.model import (
17+
ModelID,
18+
)
19+
from mindflow.core.types.model import Model
20+
21+
def configure_model():
22+
mindflow_model_ids = [
23+
MindFlowModelID.QUERY.value,
24+
MindFlowModelID.INDEX.value,
25+
MindFlowModelID.EMBEDDING.value,
26+
]
27+
mindflow_model_options: List[MindFlowModel] = [
28+
MindFlowModel.load(mindflow_model_id)
29+
for mindflow_model_id in mindflow_model_ids
30+
]
31+
mindflow_model_descriptions: List[str] = [
32+
mindflow_model.name for mindflow_model in mindflow_model_options
33+
]
34+
35+
selected_mindflow_model: MindFlowModel = select_option(
36+
"Select MindFlow model. Enter #",
37+
mindflow_model_options,
38+
mindflow_model_descriptions,
39+
)
40+
if selected_mindflow_model.id == MindFlowModelID.QUERY.value:
41+
configure_query_model()
42+
elif selected_mindflow_model.id == MindFlowModelID.INDEX.value:
43+
configure_index_model()
44+
elif selected_mindflow_model.id == MindFlowModelID.EMBEDDING.value:
45+
configure_embedding_model()
46+
47+
def configure_query_model():
48+
model_ids = [
49+
ModelID.GPT_3_5_TURBO.value,
50+
ModelID.GPT_4.value,
51+
]
52+
model_options: List[Model] = [Model.load(model_id) for model_id in model_ids]
53+
model_descriptions: List[str] = [
54+
model.config_description for model in model_options
55+
]
56+
57+
selected_model: Model = select_option(
58+
"Select chat model. Recommended GPT-4/Claude V1. Enter #",
59+
model_options,
60+
model_descriptions,
61+
)
62+
mindflow_model_config: MindFlowModelConfig = MindFlowModelConfig.load(
63+
f"{MindFlowModelID.QUERY.value}_config"
64+
) or MindFlowModelConfig(f"{MindFlowModelID.QUERY.value}_config")
65+
mindflow_model_config.model = selected_model.id
66+
mindflow_model_config.save()
67+
68+
print(f"Query Model: {selected_model.id} saved!")
69+
70+
def configure_index_model():
71+
model_ids = [
72+
ModelID.GPT_3_5_TURBO.value,
73+
ModelID.GPT_4.value,
74+
]
75+
model_options: List[Model] = [Model.load(model_id) for model_id in model_ids]
76+
model_descriptions: List[str] = [
77+
model.config_description for model in model_options
78+
]
79+
80+
selected_model: Model = select_option(
81+
"Select chat model. Recommended GPT-3.5 Turbo/Claude Instant V1. Enter #",
82+
model_options,
83+
model_descriptions,
84+
)
85+
mindflow_model_config: MindFlowModelConfig = MindFlowModelConfig.load(
86+
f"{MindFlowModelID.INDEX.value}_config"
87+
) or MindFlowModelConfig(f"{MindFlowModelID.INDEX.value}_config")
88+
mindflow_model_config.model = selected_model.id
89+
mindflow_model_config.save()
90+
91+
print(f"Index Model: {selected_model.id} saved!")
92+
93+
def configure_embedding_model():
94+
model_ids = [ModelID.TEXT_EMBEDDING_ADA_002.value]
95+
model_options: List[Model] = [Model.load(model_id) for model_id in model_ids]
96+
model_descriptions: List[str] = [
97+
model.config_description for model in model_options
98+
]
99+
100+
selected_model: Model = select_option(
101+
"Select chat model. Only one option... for now :) Enter #",
102+
model_options,
103+
model_descriptions,
104+
)
105+
mindflow_model_config: MindFlowModelConfig = MindFlowModelConfig.load(
106+
f"{MindFlowModelID.EMBEDDING.value}_config"
107+
) or MindFlowModelConfig(f"{MindFlowModelID.EMBEDDING.value}_config")
108+
mindflow_model_config.model = selected_model.id
109+
mindflow_model_config.save()
110+
111+
print(f"Embedding Model: {selected_model.id} saved!")
112+
22113
config_options = ["model"]
23114
selected_config = select_option(
24115
"What do you want to configure? Enter #", config_options, config_options
@@ -29,112 +120,6 @@ def config():
29120
save_json_store()
30121

31122

32-
def configure_model():
33-
mindflow_model_ids = [
34-
MindFlowModelID.QUERY.value,
35-
MindFlowModelID.INDEX.value,
36-
MindFlowModelID.EMBEDDING.value,
37-
]
38-
mindflow_model_options: List[MindFlowModel] = [
39-
MindFlowModel.load(mindflow_model_id)
40-
for mindflow_model_id in mindflow_model_ids
41-
]
42-
mindflow_model_descriptions: List[str] = [
43-
mindflow_model.name for mindflow_model in mindflow_model_options
44-
]
45-
46-
selected_mindflow_model: MindFlowModel = select_option(
47-
"Select MindFlow model. Enter #",
48-
mindflow_model_options,
49-
mindflow_model_descriptions,
50-
)
51-
if selected_mindflow_model.id == MindFlowModelID.QUERY.value:
52-
configure_query_model()
53-
elif selected_mindflow_model.id == MindFlowModelID.INDEX.value:
54-
configure_index_model()
55-
elif selected_mindflow_model.id == MindFlowModelID.EMBEDDING.value:
56-
configure_embedding_model()
57-
58-
59-
def configure_query_model():
60-
model_ids = [
61-
ModelID.GPT_3_5_TURBO.value,
62-
ModelID.GPT_4.value,
63-
ModelID.CLAUDE_INSTANT_V1.value,
64-
ModelID.CLAUDE_V1.value,
65-
]
66-
model_options: List[Model] = [Model.load(model_id) for model_id in model_ids]
67-
model_descriptions: List[str] = [
68-
model.config_description for model in model_options
69-
]
70-
71-
selected_model: Model = select_option(
72-
"Select chat model. Recommended GPT-4/Claude V1. Enter #",
73-
model_options,
74-
model_descriptions,
75-
)
76-
mindflow_model_config: MindFlowModelConfig = MindFlowModelConfig.load(
77-
f"{MindFlowModelID.QUERY.value}_config"
78-
) or MindFlowModelConfig(f"{MindFlowModelID.QUERY.value}_config")
79-
mindflow_model_config.model = selected_model.id
80-
mindflow_model_config.save()
81-
82-
print(f"Query Model: {selected_model.id} saved!")
83-
84-
85-
def configure_index_model():
86-
model_ids = [
87-
ModelID.GPT_3_5_TURBO.value,
88-
ModelID.GPT_4.value,
89-
ModelID.CLAUDE_INSTANT_V1.value,
90-
ModelID.CLAUDE_V1.value,
91-
]
92-
model_options: List[Model] = [Model.load(model_id) for model_id in model_ids]
93-
model_descriptions: List[str] = [
94-
model.config_description for model in model_options
95-
]
96-
97-
selected_model: Model = select_option(
98-
"Select chat model. Recommended GPT-3.5 Turbo/Claude Instant V1. Enter #",
99-
model_options,
100-
model_descriptions,
101-
)
102-
mindflow_model_config: MindFlowModelConfig = MindFlowModelConfig.load(
103-
f"{MindFlowModelID.INDEX.value}_config"
104-
) or MindFlowModelConfig(f"{MindFlowModelID.INDEX.value}_config")
105-
mindflow_model_config.model = selected_model.id
106-
mindflow_model_config.save()
107-
108-
print(f"Index Model: {selected_model.id} saved!")
109-
110-
111-
def configure_embedding_model():
112-
model_ids = [ModelID.TEXT_EMBEDDING_ADA_002.value]
113-
model_options: List[Model] = [Model.load(model_id) for model_id in model_ids]
114-
model_descriptions: List[str] = [
115-
model.config_description for model in model_options
116-
]
117-
118-
selected_model: Model = select_option(
119-
"Select chat model. Only one option... for now :) Enter #",
120-
model_options,
121-
model_descriptions,
122-
)
123-
mindflow_model_config: MindFlowModelConfig = MindFlowModelConfig.load(
124-
f"{MindFlowModelID.EMBEDDING.value}_config"
125-
) or MindFlowModelConfig(f"{MindFlowModelID.EMBEDDING.value}_config")
126-
mindflow_model_config.model = selected_model.id
127-
mindflow_model_config.save()
128-
129-
print(f"Embedding Model: {selected_model.id} saved!")
130-
131-
132-
def clear_console(lines: int):
133-
for _ in range(lines):
134-
sys.stdout.write("\033[F") # Move cursor up one line
135-
sys.stdout.write("\033[K") # Clear the line
136-
137-
138123
def select_option(prompt: str, options: List, descriptions: List[str]) -> int:
139124
for i, description in enumerate(descriptions, 1):
140125
click.echo(f"{i}: {description}")
@@ -153,3 +138,11 @@ def select_option(prompt: str, options: List, descriptions: List[str]) -> int:
153138

154139
clear_console(lines_to_clear)
155140
return options[selected_option_index - 1]
141+
142+
143+
def clear_console(lines: int):
144+
import sys
145+
146+
for _ in range(lines):
147+
sys.stdout.write("\033[F") # Move cursor up one line
148+
sys.stdout.write("\033[K") # Clear the line

0 commit comments

Comments
 (0)