Skip to content
Open
Show file tree
Hide file tree
Changes from all 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 app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ android {
exclude(group = "org.jetbrains", module = "annotations")
}
}
packaging {
resources.excludes.add("META-INF/INDEX.LIST")
resources.excludes.add("META-INF/io.netty.versions.properties")
}
}

ksp {
Expand All @@ -84,6 +88,7 @@ dependencies {

implementation(project(":smollm"))
implementation(project(":hf-model-hub-api"))
implementation(project(":smollm-server"))

// Koin: dependency injection
implementation(libs.koin.android)
Expand Down
4 changes: 4 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
<activity android:name=".ui.screens.model_download.DownloadModelActivity"/>

<activity android:name=".ui.screens.manage_tasks.ManageTasksActivity"/>
<service
android:name=".server.LlamaServerService"
android:exported="false"
/>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.shubham0204.smollmandroid.server

import android.app.Service
import android.content.Intent
import android.os.IBinder
import io.shubham0204.smollm_server.LlamaServer

class LlamaServerService : Service() {
private val llamaServer = LlamaServer()

override fun onBind(intent: Intent?): IBinder? {
TODO("Not yet implemented")
}

override fun onStartCommand(
intent: Intent?,
flags: Int,
startId: Int,
): Int {
llamaServer.start()
return START_STICKY
}

override fun onDestroy() {
llamaServer.stop()
super.onDestroy()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ import androidx.navigation.compose.rememberNavController
import io.shubham0204.smollmandroid.R
import io.shubham0204.smollmandroid.data.Chat
import io.shubham0204.smollmandroid.data.Task
import io.shubham0204.smollmandroid.server.LlamaServerService
import io.shubham0204.smollmandroid.ui.components.AppBarTitleText
import io.shubham0204.smollmandroid.ui.components.MediumLabelText
import io.shubham0204.smollmandroid.ui.screens.manage_tasks.ManageTasksActivity
Expand Down Expand Up @@ -143,6 +144,9 @@ class ChatActivity : ComponentActivity() {
}
}


startService(Intent(this, LlamaServerService::class.java))

setContent {
val navController = rememberNavController()
NavHost(
Expand Down
4 changes: 2 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.compose) apply false
alias(libs.plugins.android.library) apply false
id("com.google.devtools.ksp") version "2.0.0-1.0.24" apply false
id("com.google.devtools.ksp") version "2.0.21-1.0.28" apply false
alias(libs.plugins.jetbrains.kotlin.jvm) apply false
kotlin("plugin.serialization") version "2.1.0" apply false
kotlin("plugin.serialization") version "2.1.10" apply false
}
4 changes: 4 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ koin = "3.5.6"
koinAnnotations = "1.3.1"
jetbrainsKotlinJvm = "2.0.0"
uiTextGoogleFonts = "1.7.7"
appcompat = "1.7.0"
material = "1.12.0"


[libraries]
Expand All @@ -39,6 +41,8 @@ koin-androidx-compose = { module = "io.insert-koin:koin-androidx-compose", versi
koin-annotations = { module = "io.insert-koin:koin-annotations", version.ref = "koinAnnotations" }
koin-ksp-compiler = { module = "io.insert-koin:koin-ksp-compiler", version.ref = "koinAnnotations" }
androidx-ui-text-google-fonts = { group = "androidx.compose.ui", name = "ui-text-google-fonts", version.ref = "uiTextGoogleFonts" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }


[plugins]
Expand Down
2 changes: 1 addition & 1 deletion hf-model-hub-api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
plugins {
id("java-library")
alias(libs.plugins.jetbrains.kotlin.jvm)
kotlin("plugin.serialization") version "2.1.0"
kotlin("plugin.serialization") version "2.1.10"
}

val ktorVersion = "3.0.2"
Expand Down
3 changes: 3 additions & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import java.net.URI

include(":smollm-server")


include(":hf-model-hub-api")


Expand Down
1 change: 1 addition & 0 deletions smollm-server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
59 changes: 59 additions & 0 deletions smollm-server/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
kotlin("plugin.serialization") version "2.1.10"
id("com.google.devtools.ksp")
}

android {
namespace = "io.shubham0204.smollm_server"
compileSdk = 35

defaultConfig {
minSdk = 24

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}

buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
kotlinOptions {
jvmTarget = "21"
}
}

val ktorVersion = "3.0.3"

dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)

// Ktor server
implementation("io.ktor:ktor-server-core:$ktorVersion")
implementation("io.ktor:ktor-server-netty:$ktorVersion")

// kotlinx serialization (JSON) and
// content-negotiation
// docs: https://ktor.io/docs/server-serialization.html
implementation("io.ktor:ktor-server-content-negotiation:$ktorVersion")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")

implementation(project(":smollm"))

testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
}
Empty file.
21 changes: 21 additions & 0 deletions smollm-server/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.shubham0204.smollm_server

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("io.shubham0204.smollm_server.test", appContext.packageName)
}
}
4 changes: 4 additions & 0 deletions smollm-server/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package io.shubham0204.smollm_server

import io.ktor.http.HttpStatusCode
import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.Application
import io.ktor.server.application.install
import io.ktor.server.engine.connector
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.request.receive
import io.ktor.server.response.respond
import io.ktor.server.routing.get
import io.ktor.server.routing.post
import io.ktor.server.routing.route
import io.ktor.server.routing.routing
import io.shubham0204.smollm_server.models.OpenAIChatCompletionRequest
import kotlinx.serialization.json.Json

class LlamaServer {
private val server =
embeddedServer(
factory = Netty,
configure = {
connector {
port = 8080
}
},
) {
setupPlugins()
setupRoutes()
}

fun start() {
server.start(wait = false)
}

fun stop() {
server.stop()
}

private fun Application.setupPlugins() {
// install content-negotiation and
// configure JSON serializer
// docs: https://ktor.io/docs/server-serialization.html#install_plugin
// https://ktor.io/docs/server-serialization.html#register_xml
install(ContentNegotiation) {
json(
Json {
isLenient = true
prettyPrint = true
},
)
}
}

private fun Application.setupRoutes() {
routing {
get {
call.respond(HttpStatusCode.OK, "Hello, world!")
}
route("v1") {
route("chat") {
post("completions") {
val request = call.receive<OpenAIChatCompletionRequest>()
}
}
get("/echo") {
val message =
call.queryParameters["message"] ?: call.respond(HttpStatusCode.BadRequest)
call.respond(HttpStatusCode.OK, message)
}
get("/models") {
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.shubham0204.smollm_server.models

import kotlinx.serialization.Serializable

/**
* Represents a message in the OpenAI Chat Completion API
* docs: https://platform.openai.com/docs/api-reference/chat/create#chat-create-messages
*/
@Serializable
data class ChatCompletionMessage(
val role: String,
val content: String,
)

/**
* Represents the request for the OpenAI Chat Completion API
* docs: https://platform.openai.com/docs/api-reference/chat/create
*/
@Serializable
data class OpenAIChatCompletionRequest(
val messages: List<ChatCompletionMessage>,
val model: String,
val topP: Float = 1.0f,
val temperature: Float = 1.0f,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.shubham0204.smollm_server.models

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

/**
* OpenAI Model object, representing a LLM model available for inference
* docs: https://platform.openai.com/docs/api-reference/models/object
*/
@Serializable
data class OpenAIModel(
val id: String,
val created: Long,
@SerialName("object") val object_: String = "model", // `object` is a reserved word in Kotlin
@SerialName("owned_by") val ownedBy: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.shubham0204.smollm_server

import org.junit.Test

import org.junit.Assert.*

/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}