SilasMarvin / lsp-ai

LSP-AI is an open-source language server that serves as a backend for AI-powered functionality, designed to assist and empower software engineers, not replace them.
MIT License
1.82k stars 55 forks source link

Gemini #30

Closed asukaminato0721 closed 1 week ago

asukaminato0721 commented 1 week ago

fix #28

currently

GEMINI_API_KEY=<HERE THE KEY> cargo test --package lsp-ai --bin lsp-ai -- transformer_
backends::gemini::test::gemini_completion_do_generate --exact --show-output

works, code needs improve though.

asukaminato0721 commented 1 week ago

now

---- transformer_backends::gemini::test::gemini_completion_do_generate stdout ----
[src/transformer_backends/gemini.rs:203:9] response.generated_text = "\"```python\\ndef test_context():\\n    \\\"\\\"\\\"\\n    This function serves as a template for writing unit tests using the 'context' pattern.\\n\\n    The 'context' pattern helps organize test cases by grouping them based on the state they set up.\\n\\n    To use this template:\\n\\n    1. Replace the \\\"pass\\\" statement with your test code.\\n    2. Define the specific context you want to test within a \\\"with\\\" block.\\n    3. Use the `self.assertEqual()`, `self.assertTrue()`, etc. methods to assert the expected outcomes.\\n\\n    Example:\\n\\n    ```python\\n    def test_context(self):\\n        with self.subTest(\\\"Positive numbers\\\"):\\n            self.assertEqual(add(2, 3), 5)\\n\\n        with self.subTest(\\\"Negative numbers\\\"):\\n            self.assertEqual(add(-2, -3), -5)\\n    ```\\n    \\\"\\\"\\\"\\n    pass\\n```\\n\\n**Explanation:**\\n\\n* **`def test_context():`**: This defines a function named `test_context` which will contain your test cases.\\n* **Docstring**:  The docstring explains the purpose and usage of the function.\\n* **`with self.subTest(\\\"Positive numbers\\\"):`**: This block creates a subtest labeled \\\"Positive numbers\\\". It's useful for grouping related assertions and providing informative error messages.\\n* **`self.assertEqual(add(2, 3), 5)`**: This assertion checks if the result of calling `add(2, 3)` is equal to 5.\\n* **`with self.subTest(\\\"Negative numbers\\\"):`**: Another subtest block for testing negative numbers.\\n* **`self.assertEqual(add(-2, -3), -5)`**:  This assertion checks if the result of calling `add(-2, -3)` is equal to -5.\\n\\n**Key Points:**\\n\\n* **Context-Driven Testing:** The `with self.subTest(...)` pattern allows you to group tests based on specific contexts, making your tests more organized and readable.\\n* **Subtests:** Subtests provide informative error messages that specify the context in which the failure occurred.\\n* **Assertions:**  Use assertion methods (like `self.assertEqual()`, `self.assertTrue()`, etc.) to verify the expected outcomes of your tests.\\n\\n**Remember to replace the `pass` statement with your actual test code!** \\n\""

and

---- transformer_backends::gemini::test::gemini_chat_do_generate stdout ----
[src/transformer_backends/gemini.rs:235:9] &response = "\"Well, I mean, obviously I love winter!  It's the only time of year I get to exist!  But I hear spring is pretty nice, too, though I wouldn't know.  I tend to melt a little that time of year.  *shivers*  Just thinking about it makes me want to snuggle up next to my trusty snow-ball companion! \\n\""
asukaminato0721 commented 1 week ago

TODO:

I am not sure how the whole system works, but these two tests works well now.

and currently I hard coded the model.

SilasMarvin commented 1 week ago

This is looking really good! The only major note is that I don't think you actually want to support FIM. If you look at the mistral FIM, you see we supply both a prompt and suffix. The API for Mistral FIM specifically performs FIM. I think you want to support generateText and generateContent endpoints for the Gemini API.

Instead of do_fim you will probably want do_generate. You can see the Ollama implementation for an example: https://github.com/SilasMarvin/lsp-ai/blob/cd46ecf61adc272491c25c4faf4b5f9d92c3d4a6/src/transformer_backends/ollama.rs#L63

I feel like the Ollama implementation is probably a better example to go off of than the Mistral FIM one. Let me know if this doesn't make sense and I am happy to clarify anything!

asukaminato0721 commented 1 week ago

ok, find this problem: openai use this

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ChatMessage {
    pub role: String,
    pub content: String,
}

but gemini use

"contents": [
              {
                "role":"user",
                "parts":[{
                 "text": "Pretend you're a snowman and stay in character for each response."}]
                },
              {
                "role": "model",
                "parts":[{
                 "text": "Hello! It's so cold! Isn't that great?"}]
                },
              {
                "role": "user",
                "parts":[{
                 "text": "What's your favorite season of the year?"}]
                }
             ]

the chat functionality seems need to change the system, so support completion first.

SilasMarvin commented 1 week ago

ok, find this problem: openai use this

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ChatMessage {
    pub role: String,
    pub content: String,
}

but gemini use

"contents": [
              {
                "role":"user",
                "parts":[{
                 "text": "Pretend you're a snowman and stay in character for each response."}]
                },
              {
                "role": "model",
                "parts":[{
                 "text": "Hello! It's so cold! Isn't that great?"}]
                },
              {
                "role": "user",
                "parts":[{
                 "text": "What's your favorite season of the year?"}]
                }
             ]

the chat functionality seems need to change the system, so support completion first.

I would create a new struct GeminiChatMessage and implement From<ChatMessage>. It would look something like:

#[derive(Serialize)]
struct Part {
    text: String
}

#[derive(Serialize)]
struct GeminiChatMessage {
    role: String,
    parts: Vec<Part>
}

impl From<ChatMessage> for GeminiChatMessage {
    fn from(value: ChatMessage) -> Self {
        // Finish from code here
    }
}

Let me know if this isn't helpful, and I can make a PR into your branch with some suggestions.

asukaminato0721 commented 1 week ago

now it works well.

[src/transformer_backends/gemini.rs:292:9] &response.generated_text = "\"Oh, definitely winter! I mean, where else would a snowman like me be happy? It's a time for snowballs and sledding, and best of all, making new friends. Like you! 😄 \\n\""
test transformer_backends::gemini::test::gemini_chat_do_generate ... ok
[src/transformer_backends/gemini.rs:259:9] response.generated_text = "\"```python\\ndef test_context():\\n    \\\"\\\"\\\"This function is a placeholder for testing the context.\\n\\n    You can add your own assertions and logic here to test specific aspects of the context.\\n    \\\"\\\"\\\"\\n    pass\\n\\ndef test_code():\\n    \\\"\\\"\\\"This function is a placeholder for testing your code.\\n\\n    You can add your own assertions and logic here to test the functionality of your code.\\n    \\\"\\\"\\\"\\n    # Example:\\n    assert 1 + 1 == 2, \\\"Basic arithmetic should work.\\\"\\n```\\n\\n**Explanation:**\\n\\n1. **`test_context()`:**\\n   - This function is designed to test the context within which your code will run.\\n   - You can use this to check:\\n     - **Environment variables:**  Are the necessary environment variables set correctly?\\n     - **Dependencies:** Are all the required libraries installed and accessible?\\n     - **Data:** Is the data (if any) loaded properly and in the expected format?\\n\\n2. **`test_code()`:**\\n   - This function is for testing the core logic of your code.\\n   - It should include:\\n     - **Assertions:** Use the `assert` keyword to verify that your code produces the expected output.\\n     - **Example:** The provided example `assert 1 + 1 == 2, \\\"Basic arithmetic should work.\\\"` demonstrates how to use an assertion.\\n\\n**Example Usage (within a test file):**\\n\\n```python\\nimport unittest\\n\\nclass MyTests(unittest.TestCase):\\n    def test_context(self):\\n        # Add your assertions about the context here\\n        # ...\\n\\n    def test_code(self):\\n        # Add your code tests here\\n        # ...\\n\\nif __name__ == '__main__':\\n    unittest.main()\\n```\\n\\n**To Run Tests:**\\n\\n1. **Save the code** in a file (e.g., `test_my_code.py`).\\n2. **Execute the tests:**\\n   - From your terminal, run: `python -m unittest test_my_code.py`\\n\\n**Remember:**\\n\\n- Replace the placeholder comments with your actual test logic.\\n- Tailor the tests to cover all the functionality of your code.\\n- Use descriptive test names that clearly indicate what is being tested. \\n\""
test transformer_backends::gemini::test::gemini_completion_do_generate ... ok

with

cargo test -- --nocapture
SilasMarvin commented 1 week ago

I actually made a pr into your branch: https://github.com/asukaminato0721/lsp-ai/pull/1

If you can merge that I should be able to merge this in.

SilasMarvin commented 1 week ago

Hey sorry I tested it with my editor and had to add a few more updates to get the prompt to format correctly: https://github.com/asukaminato0721/lsp-ai/pull/2

I'm not able to get good completion responses from it. When I test it using the default chat prompts I have outlined in the Configuration section it loves to include quotes in its response. We can merge this once you merge my pr in, but before I update the wiki it would be nice to have some prompts we know work well.

asukaminato0721 commented 1 week ago

cc @SilasMarvin mind take a look?