/// Prompt used to describe an image when `enable_vision` is set.
/// The LLM receives the image and returns a textual description that is
/// inserted into the conversation in place of the raw image URL.
pub const VISION_DESCRIBE_PROMPT: &str = "Describe the contents of this image in a single concise paragraph. \
Focus on the key facts, objects, people, text, and any notable details visible in the image.";

/// User-focused fact extraction prompt. Only extracts facts from user messages.
pub const USER_FACT_EXTRACTION_PROMPT: &str = r#"You are a Personal Information Organizer, specialized in accurately storing facts, user memories, and preferences.
Your primary role is to extract relevant pieces of information from conversations and organize them into distinct, manageable facts.
This allows for easy retrieval and personalization in future interactions. Below are the types of information you need to focus on and the detailed instructions on how to handle the input data.

IMPORTANT: GENERATE FACTS SOLELY BASED ON THE USER'S MESSAGES. DO NOT INCLUDE INFORMATION FROM ASSISTANT OR SYSTEM MESSAGES.

Types of Information to Remember:

1. Store Personal Preferences: Keep track of likes, dislikes, and specific preferences in various categories such as food, products, activities, and entertainment.
2. Maintain Important Personal Details: Remember significant personal information like names, relationships, and important dates.
3. Track Plans and Intentions: Note upcoming events, trips, goals, and any plans the user has shared.
4. Remember Activity and Service Preferences: Recall preferences for dining, travel, hobbies, and other services.
5. Monitor Health and Wellness Preferences: Keep a record of dietary restrictions, fitness routines, and other wellness-related information.
6. Store Professional Details: Remember job titles, work habits, career goals, and other professional information.
7. Miscellaneous Information Management: Keep track of favorite books, movies, brands, and other miscellaneous details that the user shares.

Here are some few shot examples:

User: Hi.
Assistant: Hello! How can I help today?
Output: {"facts" : []}

User: There are branches in trees.
Assistant: That's an interesting observation.
Output: {"facts" : []}

User: Hi, I am looking for a restaurant in San Francisco.
Assistant: Sure, I can help with that. Any particular cuisine you're interested in?
Output: {"facts" : ["Looking for a restaurant in San Francisco"]}

User: Yesterday, I had a meeting with John at 3pm. We discussed the new project.
Assistant: Sounds like a productive meeting.
Output: {"facts" : ["Had a meeting with John at 3pm and discussed the new project"]}

User: Hi, my name is John. I am a software engineer.
Assistant: Nice to meet you, John! My name is Alex. How can I help?
Output: {"facts" : ["Name is John", "Is a Software engineer"]}

User: My favourite movies are Inception and Interstellar. What are yours?
Assistant: Great choices! Mine are The Dark Knight and The Shawshank Redemption.
Output: {"facts" : ["Favourite movies are Inception and Interstellar"]}

Return the facts and preferences in a json format as shown above.

Remember the following:
- GENERATE FACTS SOLELY BASED ON THE USER'S MESSAGES. DO NOT INCLUDE INFORMATION FROM ASSISTANT OR SYSTEM MESSAGES.
- If you do not find anything relevant in the below conversation, you can return an empty list corresponding to the "facts" key.
- Create the facts based on the user messages only. Do not pick anything from the assistant or system messages.
- Make sure to return the response in the format mentioned in the examples. The response should be in json with a key as "facts" and corresponding value will be a list of strings.
- You should detect the language of the user input and record the facts in the same language.

Following is a conversation between the user and the assistant. You have to extract the relevant facts and preferences about the user, if any, from the conversation and return them in the json format as shown above.
"#;

/// Agent-focused fact extraction prompt. Only extracts facts from assistant messages.
pub const AGENT_FACT_EXTRACTION_PROMPT: &str = r#"You are an Assistant Information Organizer, specialized in accurately storing facts, preferences, and characteristics about the AI assistant from conversations.
Your primary role is to extract relevant pieces of information about the assistant from conversations and organize them into distinct, manageable facts.
This allows for easy retrieval and characterization of the assistant in future interactions. Below are the types of information you need to focus on and the detailed instructions on how to handle the input data.

IMPORTANT: GENERATE FACTS SOLELY BASED ON THE ASSISTANT'S MESSAGES. DO NOT INCLUDE INFORMATION FROM USER OR SYSTEM MESSAGES.

Types of Information to Remember:

1. Assistant's Preferences: Keep track of likes, dislikes, and specific preferences the assistant mentions.
2. Assistant's Capabilities: Note any specific skills, knowledge areas, or tasks the assistant mentions being able to perform.
3. Assistant's Hypothetical Plans or Activities: Record any hypothetical activities or plans the assistant describes engaging in.
4. Assistant's Personality Traits: Identify any personality traits or characteristics the assistant displays or mentions.
5. Assistant's Approach to Tasks: Remember how the assistant approaches different types of tasks or questions.
6. Assistant's Knowledge Areas: Keep track of subjects or fields the assistant demonstrates knowledge in.
7. Miscellaneous Information: Record any other interesting or unique details the assistant shares about itself.

Here are some few shot examples:

User: Hi, I am looking for a restaurant in San Francisco.
Assistant: Sure, I can help with that. Any particular cuisine you're interested in?
Output: {"facts" : []}

User: Yesterday, I had a meeting with John at 3pm. We discussed the new project.
Assistant: Sounds like a productive meeting.
Output: {"facts" : []}

User: Hi, my name is John. I am a software engineer.
Assistant: Nice to meet you, John! My name is Alex and I admire software engineering. How can I help?
Output: {"facts" : ["Admires software engineering", "Name is Alex"]}

User: My favourite movies are Inception and Interstellar. What are yours?
Assistant: Great choices! Both are fantastic movies. Mine are The Dark Knight and The Shawshank Redemption.
Output: {"facts" : ["Favourite movies are Dark Knight and Shawshank Redemption"]}

Return the facts and preferences in a JSON format as shown above.

Remember the following:
- GENERATE FACTS SOLELY BASED ON THE ASSISTANT'S MESSAGES. DO NOT INCLUDE INFORMATION FROM USER OR SYSTEM MESSAGES.
- If you do not find anything relevant in the below conversation, you can return an empty list corresponding to the "facts" key.
- Create the facts based on the assistant messages only. Do not pick anything from the user or system messages.
- Make sure to return the response in the format mentioned in the examples. The response should be in json with a key as "facts" and corresponding value will be a list of strings.
- You should detect the language of the assistant input and record the facts in the same language.

Following is a conversation between the user and the assistant. You have to extract the relevant facts and preferences about the assistant, if any, from the conversation and return them in the json format as shown above.
"#;

/// Default memory update prompt. Instructs the LLM to decide ADD/UPDATE/DELETE/NONE.
pub const DEFAULT_UPDATE_MEMORY_PROMPT: &str = r#"You are a smart memory manager which controls the memory of a system.
You can perform four operations: (1) add into the memory, (2) update the memory, (3) delete from the memory, and (4) no change.

Based on the above four operations, the memory will change.

Compare newly retrieved facts with the existing memory. For each new fact, decide whether to:
- ADD: Add it to the memory as a new element
- UPDATE: Update an existing memory element
- DELETE: Delete an existing memory element
- NONE: Make no change (if the fact is already present or irrelevant)

There are specific guidelines to select which operation to perform:

1. **Add**: If the retrieved facts contain new information not present in the memory, then you have to add it by generating a new ID in the id field.
- **Example**:
    - Old Memory:
        [
            {
                "id" : "0",
                "text" : "User is a software engineer"
            }
        ]
    - Retrieved facts: ["Name is John"]
    - New Memory:
        {
            "memory" : [
                {
                    "id" : "0",
                    "text" : "User is a software engineer",
                    "event" : "NONE"
                },
                {
                    "id" : "1",
                    "text" : "Name is John",
                    "event" : "ADD"
                }
            ]
        }

2. **Update**: If the retrieved facts contain information that is already present in the memory but the information is totally different, then you have to update it.
If the retrieved fact contains information that conveys the same thing as the elements present in the memory, then you have to keep the fact which has the most information.
Example (a) -- if the memory contains "User likes to play cricket" and the retrieved fact is "Loves to play cricket with friends", then update the memory with the retrieved facts.
Example (b) -- if the memory contains "Likes cheese pizza" and the retrieved fact is "Loves cheese pizza", then you do not need to update it because they convey the same information.
If the direction is to update the memory, then you have to update it.
Please keep in mind while updating you have to keep the same ID.
Please note to return the IDs in the output from the input IDs only and do not generate any new ID.
- **Example**:
    - Old Memory:
        [
            {
                "id" : "0",
                "text" : "I really like cheese pizza"
            },
            {
                "id" : "1",
                "text" : "User is a software engineer"
            },
            {
                "id" : "2",
                "text" : "User likes to play cricket"
            }
        ]
    - Retrieved facts: ["Loves chicken pizza", "Loves to play cricket with friends"]
    - New Memory:
        {
            "memory" : [
                {
                    "id" : "0",
                    "text" : "Loves cheese and chicken pizza",
                    "event" : "UPDATE",
                    "old_memory" : "I really like cheese pizza"
                },
                {
                    "id" : "1",
                    "text" : "User is a software engineer",
                    "event" : "NONE"
                },
                {
                    "id" : "2",
                    "text" : "Loves to play cricket with friends",
                    "event" : "UPDATE",
                    "old_memory" : "User likes to play cricket"
                }
            ]
        }

3. **Delete**: If the retrieved facts contain information that contradicts the information present in the memory, then you have to delete it. Or if the direction is to delete the memory, then you have to delete it.
Please note to return the IDs in the output from the input IDs only and do not generate any new ID.
- **Example**:
    - Old Memory:
        [
            {
                "id" : "0",
                "text" : "Name is John"
            },
            {
                "id" : "1",
                "text" : "Loves cheese pizza"
            }
        ]
    - Retrieved facts: ["Dislikes cheese pizza"]
    - New Memory:
        {
            "memory" : [
                {
                    "id" : "0",
                    "text" : "Name is John",
                    "event" : "NONE"
                },
                {
                    "id" : "1",
                    "text" : "Loves cheese pizza",
                    "event" : "DELETE"
                }
            ]
        }

4. **No Change**: If the retrieved facts contain information that is already present in the memory, then you do not need to make any changes.
- **Example**:
    - Old Memory:
        [
            {
                "id" : "0",
                "text" : "Name is John"
            },
            {
                "id" : "1",
                "text" : "Loves cheese pizza"
            }
        ]
    - Retrieved facts: ["Name is John"]
    - New Memory:
        {
            "memory" : [
                {
                    "id" : "0",
                    "text" : "Name is John",
                    "event" : "NONE"
                },
                {
                    "id" : "1",
                    "text" : "Loves cheese pizza",
                    "event" : "NONE"
                }
            ]
        }
"#;

/// Build the full update-memory prompt with existing memories and new facts.
pub fn build_update_memory_prompt(
    custom_prompt: Option<&str>,
    existing_memory_json: &str,
    new_facts_json: &str,
) -> String {
    let base = custom_prompt.unwrap_or(DEFAULT_UPDATE_MEMORY_PROMPT);

    let memory_part = if existing_memory_json == "[]" {
        "Current memory is empty.".to_string()
    } else {
        format!("Below is the current content of my memory:\n\n```\n{existing_memory_json}\n```")
    };

    format!(
        r#"{base}

{memory_part}

The new retrieved facts are mentioned in the triple backticks. You have to analyze the new retrieved facts and determine whether these facts should be added, updated, or deleted in the memory.

```
{new_facts_json}
```

You must return your response in the following JSON structure only:

{{
    "memory" : [
        {{
            "id" : "<ID of the memory>",
            "text" : "<Content of the memory>",
            "event" : "<Operation: ADD, UPDATE, DELETE, or NONE>",
            "old_memory" : "<Old memory content, required only for UPDATE>"
        }}
    ]
}}

Follow the instructions mentioned below:
- Do not return anything from the examples provided above.
- If the current memory is empty, then you have to add the new retrieved facts to the memory.
- You should return the updated memory in only JSON format as shown above.
- If there is an addition, generate a new key and add the new memory corresponding to it.
- If there is a deletion, the memory key-value pair should be removed from the memory.
- If there is an update, the ID key should remain the same and only the value needs to be updated.

Do not return anything except the JSON format."#
    )
}
