Monday, 28 July 2025

Best Local LLMs - Ollama

Best Local LLMs for n8n, Koog, and LangChain Workflows (via Ollama):

As a developer building local AI workflows, choosing the right LLM makes all the difference—especially when balancing performance, memory, and use case fit. 

Here are some top models that run smoothly on local hardware and integrate well with tools like n8n, LangChain, or Koog.

🧠 1. Mistral (7B)
Why: Solid balance of speed and reasoning capabilities.
Use Cases: General-purpose workflows, chatbots, moderate complexity logic.
RAM: 12–16 GB
Integration: Works great with LangChain prompts or n8n HTTP nodes.
Local Storage: 4.4 GB


⚡2. Phi-3 Medium (7B)
Why: Extremely fast and lightweight; great for tight memory environments.
Use Cases: Intent classification, fast user input handling, micro-services.
RAM: 8 GB
Integration: Ideal for high-frequency calls in low-latency automation.
Local Storage: 3.0 GB (mini), 7.0 GB (medium)

🚀3. Command-R+ (7B)
Why: Purpose-built for RAG (Retrieval-Augmented Generation).
Use Cases: Document Q&A, vector DB queries, contextual chat.
RAM: 16–20 GB
Integration: Perfect if your LangChain/Koog stack pulls context before generation.
Local Storage: 17 GB

🧠 4. GPT-OSS:20B

Why: High-quality open-weight model with chain-of-thought and tool usage support.
Use Cases: Advanced automation, agent-style pipelines, deep logic chains.
RAM: 16–18 GB (slower but more capable)
Integration: Ideal for mission-critical or complex decision-making workflows.
Local Storage: 13 GB

💡These models are all available via Ollama, making local LLM workflows fast, private, and scalable. Whether you're automating business logic or prototyping AI agents, there's a model here that fits your stack and hardware.
hashtag

Thursday, 1 May 2025

Parameterized Test Vs Regular Test


                        

A parameterized test in Android (typically using JUnit4 or JUnit5 with AndroidX Test) is best used when you want to run the same test logic multiple times with different inputs. This helps improve code coverage and ensures your logic works correctly across a variety of inputs.



Regular Test Fixtures:




Drawbacks:
  • Fails Entire Loop on First Assertion Failure
  • No Granular Reporting in Test Output
  • Harder to Debug, Stack traces are less helpful than in named parameterized methods.
  • You can’t click-to-run or inspect individual input-output combinations in the test runner
  • Manual Maintenance, we manually iterating, matching, and asserting.

Sunday, 21 August 2022

Jetpack Compose UI – Stateful, Stateless and State Hoisting







Jetpack Compose is a declarative approach of design and code, where as the typical imperative programming era moving to parallel universe of declarative programming.  Here on this process, it gives more impact on the UX and it rendering process, so it closely works on the single changes that would have to be repaint i.e. re-construct the UI as fully or partially whichever the use case demands.  So, the changes would be caught and works with UI States and these are works on the parameters of events and observers.  Here we going to see where and how the state works in our android app. 


Before we go into these details of Stateful and Stateless, we have to understand fewer things which connects the State and Composables. 


State on Composition & Re-Composition:  Compose is declarative one and through it will also calling the same composable with the new arguments on demand of changes, the arguments are the real representatives of this UI state. It would be anything the updates made on the state, the recomposition of UI will take place. 


For Example, things like TextField, Checkbox, and Option etc... As like as imperative programming (XML layout views) these widgets won’t automatically update while on selecting/toggling in compose. Here in a composable we have to explicitly tell the new state as value changed in order update. 


How the State Drive On: Composable functions can use the remember APIs to store an object in memory. A value computed by remember is stored in the Composition during initial composition, and the stored value is returned during recomposition. remember can be used to store both mutable and immutable objects, these objects are defined with mutableStateOf which is an observable type integrated with the compose runtime.


Now coming to our discussion point, how and what would be the Stateful and Stateless Compose?


Here is the screenshot of the use case and code snippet for both Stateful and Stateless compose will be as follows, 



Simple logic is in the initial screen click me is an action button it will keep increase by 1 on each click, on the regular interval of every 5 count and the ahead a step of 2, that text “I’m Visible between x to y” will be displayed and this keeps on continuing. (Refer this branch for the full Source code)


Stateful: Stateful is when composable uses remember to store an object i.e. create, holds and modifies its own State internally.  Stateful is useful when we don’t need to control the State of a composable.  However, the weakness of Stateful is that it tends to be less reusable and difficult to test.  


Here in the below code the state, ui and logic is completely packed into one single compose function. 


Stateless:  Stateless composable is a composable that doesn't hold any state, rather it only receive & hoist the state and this pattern is called State hoisting.  State hoisting in Compose is a pattern of moving state to a composable's caller to make a composable stateless.  Another thing by using this State Hoisting approach, we can easily mock the parent method by using Mockito and test our parent and child methods

Benefits of State Hoisting:
  • By Moving state instead of duplicating it, we’re ensuring there’s only one source of truth. This helps avoid bugs.
  • Only stateful composable will be able to modify their state. It’s completely internally encapsulated.
  • Hoisted states can be shared with multiple composable.
  • Only the callers to the stateless composable can decide to ignore or modify events before changing the state, easy to intercept
Here in the below code snippet, we have 3 compose functions (1 stateful and 2 stateless), maybe we can keep it as 2 compose, but I just wanted to make it intentionally 3 because want to separate the UI into two different stateless and passing state param and action as lambda param to the functions.


Statement from the official docs

“As we develop reusable Composables, we often want to expose both a Stateful and a Stateless version of the same composable. The Stateful version is convenient for callers that don’t care about the state, and the Stateless version is necessary for callers that need to control or hoist the State.”

Above Code Ref Gist: Sateful, StateHoisting

Sunday, 3 July 2022

LiveData & Flow

As part of developing apps on android, it is quite important things is how effectively and efficiently the UI can be updated on demand to the changes in data at lower layers, decoupling those layers, handling callbacks. Separation of these layers really helps on the testing part. (i.e. constructing the domain and data layer independently).   Since the legacy android code i.e. code which is written on imperative styles we would have writing lot of boilerplate code to handle the data changes using AsyncTask, callbacks, handlers etc.  But now the development has notably evolved and I would say much easier if one understands the basics.  There are always multiple ways to perform a given task within android and one can only decide the best alternative if the basic difference, pros, and cons of all options are mastered.


Just Glimpses:


LiveData: LiveData is a lifecycle aware observable data holder (means it knows the lifecycle of the activity or a fragment) use it when you play with UI elements (views).


Flow: Flow (cold stream) – In general think of it like a stream of data flowing in a pipe with both ends having a producer and consumer running on a coroutines.


StateFlow: (hot stream) does similar things like LiveData but it is made using flow by kotlin guys and only difference compare to LiveData is it’s not lifecycle aware but this is also been solved using repeatOnLifecycle api’s, so whatever LiveData can do StateFlow can do much better with power of flow’s api. StateFlow won’t emit same value.


SharedFlow: (hot stream) – name itself says it is shared, this flow can be shared by multiple consumers, I mean if multiple collect calls happening on the sharedflow there will be a single flow which will get shared across all the consumers, unlike normal flow.


What are the above highlighted terms, let’s try to address here what are those in details and that helps us to choose among LiveData, Flow, SharedFlow, and StateFlow


LiveData:  Live data is part of Android Architecture Components.  


It is an Observable data class:  it can hold data and that data can be observed from android components. Meaning it can be observed by other components — most profoundly UI controllers (Activities/Fragments).  


It is Lifecycle aware—it sends updates to our components i.e. UI (Activities/Fragments) only when our view is in the active state.

 

Pros

Cons

  • Would always get the latest data
  • Always check if the subscriber is active before publishing data.
  • No memory leaks
  • No crashes or unnecessary ANR
  • Configuration changes update
  • Lack of control over the execution context
  • Threading issue especially when used in Repositories
  • Not built on top of Coroutines and Kotlin
  • Lack of seamless data integration across between database and UI especially using Room.
  • Lots of Boiler Plate Codes especially while using Transformations

 


Flow:  Built on top of a coroutines, a flow emits multiple values sequentially.  It’s a stream of data that can be computed sequentially.  Flow can handle streams of values, and transform data in complex multi-threaded ways using an intermediate operators to modify the stream without consuming values. (So basically it’s an alternate to RxJava.)

 

Flows by nature are not lifecycle aware unlike LiveData. Which makes sense as it’s not a part of android component but a type from Kotlin language. However, this can be resolved by responsibly collecting flow values within lifeCycleScopes via coroutines.

 

Flow is declarative/cold: It can only be executed on collection and there are hot flows as well (SharedFlow and StateFlow).

 

COLD: Stops emission when any collector is not active. HOT: It remains in memory as long as the flow is collected or as long as any other references to it exist from a garbage collection root.

 

StateFlow:

 

  • State Flow is similar to normal flow but it holds the state. When you initialize the state flow you always need to tell the initial value of state flow. Hence state flow will have always a value.
  • State flow is Hot Flow because it starts emitting values even though there is no consumer and those data will be remains in the memory
  • State flow always has an initial value, replays one most recent value to new subscribers, does not buffer any more values, but keeps the last emitted one.
  • All methods of state flow are thread-safe and can be safely invoked from concurrent coroutines without external synchronization.
  • When you collect state flow through collect{} and if the consumer gets recreated due to configuration change it will re-call collect{}
  • You can convert Cold flow to state flow using stateIn () operator.

SharedFlow:

  • A SharedFlow is a highly-configurable generalization of StateFlow, that emits all value to all consumers in a broadcast fashion.
  • A shared flow keeps a specific number of the most recent values in its replay cache. Every new subscriber first gets the values from the replay cache and then gets new emitted values. The maximum size of the replay cache is specified when the shared flow is created by the replay parameter.
  • A default implementation of a shared flow that is created with MutableSharedFlow() constructor function without parameters has no replay cache nor additional buffer.
  • All methods of shared flow are thread-safe and can be safely invoked from concurrent coroutines without external synchronization.
  • When you collect shared flow through collect{} and if the consumer gets recreated due to configuration change it will not re-call collect{}
  • You can convert Cold flow to shared flow using shareIn () operator.

Extras….


StateFlow and LiveData have similarities: Both are observable data holder classes, and both follow a similar pattern when used in your app architecture.

The StateFlow and LiveData do behave differently: StateFlow requires an initial state to be passed into the constructor, while LiveData does not.


LiveData.observe() automatically unregisters the consumer when the view goes to the STOPPED state, whereas collecting from a StateFlow or any other flow does not stop collecting automatically. To achieve the same behavior,you need to collect the flow from a Lifecycle.repeatOnLifecycle block.


StateFlow Vs. SharedFlow: The main difference between a SharedFlow and a StateFlow is that a StateFlow takes a default value through the constructor and emits it immediately when someone starts collecting, while a SharedFlow takes no value and emits nothing by default.


Here we see the code snippet in the blog using LiveData, SharedFlow and StateFlow used in ViewModel and that how it handles with the retrofit response and updates the same to the Fragment.


Full Source Code Reference in Branch



updateView(..) in the Fragment (Code Snippet to update the UI View handle)


private fun updateView(res: Resource<Summary>) {
        when (res.status) {
            Status.LOADING -> {
                binding.progressBar.visibility = View.VISIBLE
            }
            Status.SUCCESS -> {
                binding.progressBar.visibility = View.GONE
                res.data?.let {
                    updateCards(it.global)
                    it.countries?.let {
                        statusAdapter.setData(it)
                    } ?: kotlin.run {
                        Toast.makeText(activity, "No Data Available " + res.msg, Toast.LENGTH_LONG)
                            .show()
                    }
                }
            }
            Status.ERROR -> {
                binding.progressBar.visibility = View.GONE
                Toast.makeText(
                    activity,
                    "Something went wrong... Please contact admin " + res.msg,
                    Toast.LENGTH_LONG
                ).show()
            }
        }
    }

LiveData: In ViewModel class:

private val _summaryLiveData = MutableLiveData(Resource.loading(Summary()))
val summaryLiveData = _summaryLiveData as LiveData<Resource<Summary>>

// LiveData Object gets updated

fun getCovidStatusLiveData() {
        viewModelScope.launch(Dispatchers.IO) {
            try {
                _summaryLiveData.postValue(Resource.loading(null))
                val summaryResponse =
                    APIClient.createService(tClass = CovidStatusAPI::class.java)
                        .getCountriesSummary()
                summaryResponse.takeIf { it.isSuccessful }?.let {
                    _summaryLiveData.postValue(Resource.success(it.body() as Summary))
                } ?: kotlin.run {
                    _summaryLiveData.postValue(Resource.error("Error", null))
                }
            } catch (e: Exception) {
                _summaryLiveData.postValue(Resource.error(e.message ?: "Err", null))
            }
        }
    }

// Using LiveData Scope (It uses emits)
fun getCovidStatusLiveDataScope() = liveData {
        try {
            emit(Resource.loading(null))
            val summaryResponse =
                APIClient.createService(tClass = CovidStatusAPI::class.java).getCountriesSummary()
            summaryResponse.takeIf { it.isSuccessful }?.let {
                emit(Resource.success(it.body() as Summary))
            } ?: kotlin.run {
                emit(Resource.error("Error", null))
            }
        } catch (e: Exception) {
            emit(Resource.error(e.message ?: "Err"))
        }
    }

In Fragment (LiveData observer):

// Observing the LiveData object of viewmodel 
viewModel.summaryLiveData.observe(viewLifecycleOwner) {
            updateView(it)
        }

// Observing the direct livedata scope function of viewmodel 
viewModel.getCovidStatusLiveDataScope().observe(viewLifecycleOwner){
            updateView(it)
        }

StateFlow & SharedFlow: In ViewModel class:

private val _summaryStateFlow = MutableStateFlow(Resource.loading(Summary()))
val summaryStateFlow = _summaryStateFlow as StateFlow<Resource<Summary>>

private val _summarySharedFlow = MutableSharedFlow<Resource<Summary>>()
val summarySharedFlow = _summarySharedFlow as SharedFlow<Resource<Summary>>

// Using SharedFlow
fun getCovidStatusSharedFlow() {
        viewModelScope.launch(Dispatchers.IO) {
            try {
                _summarySharedFlow.emit(Resource.loading(null))
                val summaryResponse =
                        APIClient.createService(tClass = CovidStatusAPI::class.java)
                                .getCountriesSummary()
                summaryResponse.takeIf { it.isSuccessful }?.let {
                    _summarySharedFlow.emit(Resource.success(it.body() as Summary))
                } ?: kotlin.run {
                    _summarySharedFlow.emit(Resource.error("Error", null))
                }
            } catch (e: Exception) {
                _summarySharedFlow.emit(Resource.error(e.message ?: "Err", null))
            }
        }
    }

// Using StateFlow
fun getCovidStatusStateFlow() {
        viewModelScope.launch(Dispatchers.IO) {
            try {
                _summaryStateFlow.emit(Resource.loading(null))
                val summaryResponse =
                        APIClient.createService(tClass = CovidStatusAPI::class.java)
                                .getCountriesSummary()
                summaryResponse.takeIf { it.isSuccessful }?.let {
                    _summaryStateFlow.emit(Resource.success(it.body() as Summary))
                } ?: kotlin.run {
                    _summaryStateFlow.emit(Resource.error("Error", null))
                }
            } catch (e: Exception) {
                _summaryStateFlow.value = Resource.error(e.message ?: "Err", null)
            }
        }
    }

In Fragment (Handling state and shared flow emitted values): Since we know Flows by nature are not lifecycle aware, so it need to be handled like below.

// StateFlow to make it as lifecycle aware.
viewLifecycleOwner.lifecycleScope.launch {
             viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                 launch {
                     viewModel.summaryStateFlow
                         .collectLatest {
                             updateView(it)
                         }
                 }
             }
         }

// SharedFlow to make it as lifecycle aware.
viewLifecycleOwner.lifecycleScope.launch {
             viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                 launch {
                     viewModel.summarySharedFlow.collectLatest {
                         updateView(it)
                     }
                 }
             }
         }

But doing above for all stateflow or sharedflow object is tedious so we can write one common inline function in Utils to make it as easy for the lifetime.

// This Take care of LifeCycle Aware
inline fun <T> Flow<T>.launchAndCollectIn(
    owner: LifecycleOwner,
    minState: Lifecycle.State = Lifecycle.State.STARTED,
    crossinline block: suspend CoroutineScope.(T) -> Unit
) {
    owner.lifecycleScope.launch {
        owner.repeatOnLifecycle(minState) {
            collect {
                block(it)
            }
        }
    }
}

viewModel.summarySharedFlow.launchAndCollectIn(viewLifecycleOwner) {
            updateView(it)
        }

viewModel.summaryStateFlow.launchAndCollectIn(viewLifecycleOwner) {
            updateView(it)
        }