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

feat: Make suggest next questions configurable #275

Merged
merged 10 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cyan-buttons-clean.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"create-llama": patch
---

Add env config for next questions feature
51 changes: 23 additions & 28 deletions helpers/env-variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -486,34 +486,29 @@ It\\'s cute animal.
return systemPromptEnv;
};

const getTemplateEnvs = (template?: TemplateType): EnvVar[] => {
if (template === "multiagent") {
leehuwuj marked this conversation as resolved.
Show resolved Hide resolved
return [
{
name: "MESSAGE_QUEUE_PORT",
},
{
name: "CONTROL_PLANE_PORT",
},
{
name: "HUMAN_CONSUMER_PORT",
},
{
name: "AGENT_QUERY_ENGINE_PORT",
value: "8003",
},
{
name: "AGENT_QUERY_ENGINE_DESCRIPTION",
value: "Query information from the provided data",
},
{
name: "AGENT_DUMMY_PORT",
value: "8004",
},
];
} else {
return [];
const getTemplateEnvs = (
template?: TemplateType,
framework?: TemplateFramework,
): EnvVar[] => {
const nextQuestionEnvs: EnvVar[] = [
{
name: "NEXT_QUESTION_PROMPT",
description: `Customize prompt to generate the next question suggestions based on the conversation history.
Disable this prompt to disable the next question suggestions feature.`,
value: `"You're a helpful assistant! Your task is to suggest the next question that user might ask.
Here is the conversation history
---------------------\n{conversation}\n---------------------
Given the conversation history, please give me 3 questions that you might ask next!"`,
},
];

if (
framework === "fastapi" &&
(template === "multiagent" || template === "streaming")
) {
return nextQuestionEnvs;
}
return [];
Copy link

Choose a reason for hiding this comment

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

Review of getTemplateEnvs function modifications

The modifications to the getTemplateEnvs function introduce a new environment variable NEXT_QUESTION_PROMPT and extend the function's capability to handle different templates. Here are some observations and suggestions:

  1. Correctness: The function correctly checks for the framework and template conditions before returning the nextQuestionEnvs. This ensures that the environment variables are only added when necessary, which is good for avoiding unnecessary configuration in other scenarios.

  2. Maintainability: The function is straightforward and maintains readability with clear condition checks and structured environment variable definitions. However, consider adding more comments explaining why only "multiagent" and "streaming" templates are considered here.

  3. Performance: There are no performance concerns with the current implementation as the operations are primarily conditional checks and array manipulations.

  4. Best Practices: The use of template literals and structured data for environment variables is a good practice. It keeps the code clean and easy to understand.

  5. Potential Improvements:

    • Extensibility: If more templates or conditions are expected in the future, consider refactoring this into a more scalable solution, such as using a mapping object or a switch-case structure to handle different templates.
    • Error Handling: Currently, the function does not handle potential errors or unexpected values for template or framework. Adding error logging or warnings for unexpected values could improve robustness.

Overall, the changes align well with the PR objectives and improve the configurability of the next questions feature.

Consider the following refactor for future extensibility and error handling:

// Potential refactor for handling multiple templates with error logging
const TEMPLATE_ENV_CONFIGS = {
  multiagent: nextQuestionEnvs,
  streaming: nextQuestionEnvs,
  // Add more templates as needed
};

function getTemplateEnvs(template?: TemplateType, framework?: TemplateFramework): EnvVar[] {
  if (framework === "fastapi" && TEMPLATE_ENV_CONFIGS[template]) {
    return TEMPLATE_ENV_CONFIGS[template];
  } else {
    console.warn(`Unsupported template or framework: ${template}, ${framework}`);
    return [];
  }
}

};

const getObservabilityEnvs = (
Expand Down Expand Up @@ -560,7 +555,7 @@ export const createBackendEnvFile = async (
...getVectorDBEnvs(opts.vectorDb, opts.framework),
...getFrameworkEnvs(opts.framework, opts.externalPort),
...getToolEnvs(opts.tools),
...getTemplateEnvs(opts.template),
...getTemplateEnvs(opts.template, opts.framework),
...getObservabilityEnvs(opts.observability),
...getSystemPromptEnv(opts.tools, opts.dataSources, opts.framework),
];
Expand Down
7 changes: 7 additions & 0 deletions helpers/python.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,13 @@ export const installPythonTemplate = async ({
cwd: path.join(compPath, "settings", "python"),
});

// Copy services
if (template == "streaming" || template == "multiagent") {
await copy("**", path.join(root, "app", "api", "services"), {
cwd: path.join(compPath, "services", "python"),
});
}
leehuwuj marked this conversation as resolved.
Show resolved Hide resolved

if (template === "streaming") {
// For the streaming template only:
// Select and copy engine code based on data sources and tools
Expand Down
leehuwuj marked this conversation as resolved.
Show resolved Hide resolved
File renamed without changes.
leehuwuj marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
import logging
from typing import List
import os
from typing import List, Optional

from app.api.routers.models import Message
from llama_index.core.prompts import PromptTemplate
from llama_index.core.settings import Settings
from pydantic import BaseModel

NEXT_QUESTIONS_SUGGESTION_PROMPT = PromptTemplate(
"You're a helpful assistant! Your task is to suggest the next question that user might ask. "
"\nHere is the conversation history"
"\n---------------------\n{conversation}\n---------------------"
"Given the conversation history, please give me {number_of_questions} questions that you might ask next!"
)
N_QUESTION_TO_GENERATE = 3


logger = logging.getLogger("uvicorn")


Expand All @@ -25,15 +17,24 @@ class NextQuestions(BaseModel):


class NextQuestionSuggestion:
@staticmethod

@classmethod
def get_configured_prompt(cls) -> Optional[str]:
return os.getenv("NEXT_QUESTION_PROMPT", None)

@classmethod
async def suggest_next_questions(
cls,
messages: List[Message],
number_of_questions: int = N_QUESTION_TO_GENERATE,
) -> List[str]:
) -> Optional[List[str]]:
"""
Suggest the next questions that user might ask based on the conversation history
Return as empty list if there is an error
Return None if suggestion is disabled or there is an error
"""
prompt_template = cls.get_configured_prompt()
if not prompt_template:
return None

try:
# Reduce the cost by only using the last two messages
last_user_message = None
Expand All @@ -49,12 +50,11 @@ async def suggest_next_questions(

output: NextQuestions = await Settings.llm.astructured_predict(
marcusschiesser marked this conversation as resolved.
Show resolved Hide resolved
NextQuestions,
prompt=NEXT_QUESTIONS_SUGGESTION_PROMPT,
prompt=PromptTemplate(prompt_template),
conversation=conversation,
number_of_questions=number_of_questions,
)

return output.questions
except Exception as e:
logger.error(f"Error when generating next question: {e}")
return []
return None
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
from asyncio import Task
import json
import logging
from asyncio import Task
from typing import AsyncGenerator

from aiostream import stream
from app.agents.single import AgentRunEvent, AgentRunResult
from app.api.routers.models import ChatData, Message
from app.api.services.suggestion import NextQuestionSuggestion
from fastapi import Request
from fastapi.responses import StreamingResponse

from app.api.routers.models import ChatData
from app.agents.single import AgentRunEvent, AgentRunResult

logger = logging.getLogger("uvicorn")


Expand Down Expand Up @@ -57,16 +57,32 @@ async def content_generator(
# Yield the text response
async def _chat_response_generator():
result = await task
final_response = ""

if isinstance(result, AgentRunResult):
for token in result.response.message.content:
yield VercelStreamResponse.convert_text(token)

if isinstance(result, AsyncGenerator):
async for token in result:
final_response += token.delta
yield VercelStreamResponse.convert_text(token.delta)

# TODO: stream NextQuestionSuggestion
# Generate next questions if next question prompt is configured
if NextQuestionSuggestion.get_configured_prompt() is not None:
conversation = chat_data.messages + [
Message(role="assistant", content=final_response)
]
questions = await NextQuestionSuggestion.suggest_next_questions(
conversation
)
if questions:
yield VercelStreamResponse.convert_data(
{
"type": "suggested_questions",
"data": questions,
}
)
# TODO: stream sources
leehuwuj marked this conversation as resolved.
Show resolved Hide resolved

# Yield the events from the event handler
Expand Down
1 change: 1 addition & 0 deletions templates/types/multiagent/fastapi/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ llama-index = "^0.11.4"
fastapi = "^0.112.2"
python-dotenv = "^1.0.0"
uvicorn = { extras = ["standard"], version = "^0.23.2" }
pydantic-settings = "^2.4.0"
cachetools = "^5.3.3"
aiostream = "^0.5.2"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,21 @@ async def _chat_response_generator():
final_response += token
yield VercelStreamResponse.convert_text(token)

# Generate questions that user might interested to
conversation = chat_data.messages + [
Message(role="assistant", content=final_response)
]
questions = await NextQuestionSuggestion.suggest_next_questions(
conversation
)
if len(questions) > 0:
yield VercelStreamResponse.convert_data(
{
"type": "suggested_questions",
"data": questions,
}
# Generate next questions if next question prompt is configured
if NextQuestionSuggestion.get_configured_prompt() is not None:
conversation = chat_data.messages + [
Message(role="assistant", content=final_response)
]
questions = await NextQuestionSuggestion.suggest_next_questions(
conversation
)
if questions:
yield VercelStreamResponse.convert_data(
{
"type": "suggested_questions",
"data": questions,
}
)

# the text_generator is the leading stream, once it's finished, also finish the event stream
event_handler.is_done = True
Expand Down
3 changes: 2 additions & 1 deletion templates/types/streaming/fastapi/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ fastapi = "^0.109.1"
uvicorn = { extras = ["standard"], version = "^0.23.2" }
python-dotenv = "^1.0.0"
aiostream = "^0.5.2"
llama-index = "0.11.6"
pydantic-settings = "^2.4.0"
cachetools = "^5.3.3"
llama-index = "0.11.6"

[build-system]
requires = ["poetry-core"]
Expand Down
Loading