lib: expose GgufMetadataReader as interface only
This commit is contained in:
parent
6a5bc94ff1
commit
130cba9aa6
|
|
@ -107,6 +107,7 @@ class ModelRepositoryImpl @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
private val modelDao: ModelDao,
|
private val modelDao: ModelDao,
|
||||||
private val huggingFaceRemoteDataSource: HuggingFaceRemoteDataSource,
|
private val huggingFaceRemoteDataSource: HuggingFaceRemoteDataSource,
|
||||||
|
private val ggufMetadataReader: GgufMetadataReader,
|
||||||
) : ModelRepository {
|
) : ModelRepository {
|
||||||
|
|
||||||
private val modelsDir = File(context.filesDir, INTERNAL_STORAGE_PATH)
|
private val modelsDir = File(context.filesDir, INTERNAL_STORAGE_PATH)
|
||||||
|
|
@ -224,7 +225,7 @@ class ModelRepositoryImpl @Inject constructor(
|
||||||
val metadata = try {
|
val metadata = try {
|
||||||
val filePath = modelFile.absolutePath
|
val filePath = modelFile.absolutePath
|
||||||
Log.i(TAG, "Extracting GGUF Metadata from $filePath")
|
Log.i(TAG, "Extracting GGUF Metadata from $filePath")
|
||||||
GgufMetadata.fromDomain(GgufMetadataReader().readStructuredMetadata(filePath))
|
GgufMetadata.fromDomain(ggufMetadataReader.readStructuredMetadata(filePath))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Cannot extract GGUF metadata: ${e.message}", e)
|
Log.e(TAG, "Cannot extract GGUF metadata: ${e.message}", e)
|
||||||
throw e
|
throw e
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.example.llama.di
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.llama.cpp.InferenceEngine
|
import android.llama.cpp.InferenceEngine
|
||||||
import android.llama.cpp.InferenceEngineLoader
|
import android.llama.cpp.InferenceEngineLoader
|
||||||
|
import android.llama.cpp.gguf.GgufMetadataReader
|
||||||
import com.example.llama.data.local.AppDatabase
|
import com.example.llama.data.local.AppDatabase
|
||||||
import com.example.llama.data.remote.HuggingFaceApiService
|
import com.example.llama.data.remote.HuggingFaceApiService
|
||||||
import com.example.llama.data.remote.HuggingFaceRemoteDataSource
|
import com.example.llama.data.remote.HuggingFaceRemoteDataSource
|
||||||
|
|
@ -82,6 +83,10 @@ internal abstract class AppModule {
|
||||||
@Provides
|
@Provides
|
||||||
fun providesSystemPromptDao(appDatabase: AppDatabase) = appDatabase.systemPromptDao()
|
fun providesSystemPromptDao(appDatabase: AppDatabase) = appDatabase.systemPromptDao()
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun providesGgufMetadataReader(): GgufMetadataReader = GgufMetadataReader.create()
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideOkhttpClient() = OkHttpClient.Builder()
|
fun provideOkhttpClient() = OkHttpClient.Builder()
|
||||||
|
|
|
||||||
|
|
@ -4,23 +4,62 @@ import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
|
||||||
private val DEFAULT_SKIP_KEYS = setOf(
|
/**
|
||||||
"tokenizer.chat_template",
|
* Interface for reading GGUF metadata from model files.
|
||||||
"tokenizer.ggml.scores",
|
* Use `GgufMetadataReader.create()` to get an instance.
|
||||||
"tokenizer.ggml.tokens",
|
*/
|
||||||
"tokenizer.ggml.token_type"
|
interface GgufMetadataReader {
|
||||||
)
|
/**
|
||||||
|
* Reads and parses GGUF metadata from the specified file path.
|
||||||
|
*
|
||||||
|
* @param path The absolute path to the GGUF file
|
||||||
|
* @return Structured metadata extracted from the file
|
||||||
|
* @throws IOException if file cannot be read
|
||||||
|
* @throws IllegalArgumentException if file format is invalid
|
||||||
|
*/
|
||||||
|
suspend fun readStructuredMetadata(path: String): GgufMetadata
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val DEFAULT_SKIP_KEYS = setOf(
|
||||||
|
"tokenizer.chat_template",
|
||||||
|
"tokenizer.ggml.scores",
|
||||||
|
"tokenizer.ggml.tokens",
|
||||||
|
"tokenizer.ggml.token_type"
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a default GgufMetadataReader instance
|
||||||
|
*/
|
||||||
|
fun create(): GgufMetadataReader = GgufMetadataReaderImpl(
|
||||||
|
skipKeys = DEFAULT_SKIP_KEYS,
|
||||||
|
arraySummariseThreshold = 1_000
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a GgufMetadataReader with custom configuration
|
||||||
|
*
|
||||||
|
* @param skipKeys Keys whose value should be skipped entirely (not kept in the result map)
|
||||||
|
* @param arraySummariseThreshold If ≥0, arrays longer get summarised, not materialised;
|
||||||
|
* If -1, never summarise.
|
||||||
|
*/
|
||||||
|
fun create(
|
||||||
|
skipKeys: Set<String> = DEFAULT_SKIP_KEYS,
|
||||||
|
arraySummariseThreshold: Int = 1_000
|
||||||
|
): GgufMetadataReader = GgufMetadataReaderImpl(
|
||||||
|
skipKeys = skipKeys,
|
||||||
|
arraySummariseThreshold = arraySummariseThreshold
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class to read GGUF model files and extract metadata key-value pairs.
|
* Utility class to read GGUF model files and extract metadata key-value pairs.
|
||||||
* This parser reads the header and metadata of a GGUF v3 file (little-endian) and skips tensor data.
|
* This parser reads the header and metadata of a GGUF v3 file (little-endian) and skips tensor data.
|
||||||
*/
|
*/
|
||||||
class GgufMetadataReader(
|
private class GgufMetadataReaderImpl(
|
||||||
/** Keys whose value should be skipped entirely (not kept in the resulting map). */
|
private val skipKeys: Set<String>,
|
||||||
private val skipKeys: Set<String> = DEFAULT_SKIP_KEYS,
|
private val arraySummariseThreshold: Int,
|
||||||
/** If ≥0, arrays longer than this get summarised instead of materialised. -1 ⇒ never summarise. */
|
) : GgufMetadataReader {
|
||||||
private val arraySummariseThreshold: Int = 1_000
|
|
||||||
) {
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val ARCH_LLAMA = "llama"
|
private const val ARCH_LLAMA = "llama"
|
||||||
}
|
}
|
||||||
|
|
@ -92,7 +131,7 @@ class GgufMetadataReader(
|
||||||
* @throws IOException if the file is not GGUF, the version is unsupported,
|
* @throws IOException if the file is not GGUF, the version is unsupported,
|
||||||
* or the metadata block is truncated / corrupt.
|
* or the metadata block is truncated / corrupt.
|
||||||
*/
|
*/
|
||||||
fun readStructuredMetadata(path: String): GgufMetadata {
|
override suspend fun readStructuredMetadata(path: String): GgufMetadata {
|
||||||
File(path).inputStream().buffered().use { input ->
|
File(path).inputStream().buffered().use { input ->
|
||||||
// ── 1. header ──────────────────────────────────────────────────────────
|
// ── 1. header ──────────────────────────────────────────────────────────
|
||||||
// throws on mismatch
|
// throws on mismatch
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue