At work, there is a need to migrate our project from using LoganSquare to kotlinx.serialization.
Part of the work involves replacing the annotations the first library is using with the ones from the second. Unfortunately some cases are not as simple as replacing foo with boo. For example, a property must be annotated with @JsonField(name = ["a_name_here"]) in LoganSquare and @SerialName("a_name_here") is kotlinx.
So, I had to decide:
- Do I spend 2-3 hours and migrate 100+ files manually, one by one?
- Do I cut the hours in half by using the search and replace tool and then fix anything the tool couldn’t manage?
- Do I start a journey of unknown number of hours to figure out how to perform the migration using a local LLM?
Ollama
Yeap, I chose to go with number three! And to do that I started by installing Ollama. Ollama is a tool that allows you to download open source LLMs and start playing with them locally. All prompts are handled in your device and there is no network activity.
You can either download it from its site or, if you are on macOS, use brew: brew install ollama.
After that you can run one of the models it provides, ex: ollama run llama3.2
or fire up the server it comes with and start playing with its API: ollama serve
Kotlin
The flow is simple:
- Load in memory, one by one, the contents of the files that must be migrated
- Provide each content along side with a prompt to an LLM
- Store the LLM’s result to the file
- (optional) Start dancing for building your first LLM based workflow
Reading the contents and writing them back to the files is easy with Kotlin. Communicating with the ollama server is also easy when using OkHttp and kotlinx.serialization. Believe it or not the most time consuming part was figuring out the prompt!
After a lot of attempts the one prompt that managed to produced the best result was the one where I listed the steps that I would have done manually:
We have a file written in Kotlin and we need to migrate it from LoganSquare to KotlinX Serialization.
To do that we have to replace:
- "import com.bluelinelabs.logansquare.annotation.JsonField" with "import kotlinx.serialization.SerialName"
- "import com.bluelinelabs.logansquare.annotation.JsonObject" with "import kotlinx.serialization.Serializable"
- "@JsonObject\ninternal class <class name>" with "@Serializable\ninternal class <class name>"
- "@JsonObject\nclass <class name>" with "@Serializable\nclass <class name>"
- "@JsonField(name = ["<property name>"])" with "@SerialName("<property name>")"
Everything else in the file should be copied without any changes.
Please migrate the following file:
$contents
We just want the file. Don't comment on the result.
and even then, small details did matter a lot.
For example, at the beginning of the prompt I refer to a file but later in the text I was saying Please migrate the following class. That alone was resulting in various weird migrations where a class was either missing completely or had only half of its initial code. Same results when I wasn’t using \n after the annotations.
The code
Conclusion
Was I faster than choice number two? Didn’t try this choice but I guess no. Too many things to learn, figure out and write.
Do I regret it? No! I now have a new tool in my belt and I’m pretty sure it will pay off, time wise, in the future.
ollama-kotlin-playground
One more thing that came out of this endeavour is ollama-kotlin-playground. A very simple library that does only one thing: generate a completion without even supporting all possible parameters. It is my way of not copying code from one tool/experiment to another.