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,
|
||||
private val modelDao: ModelDao,
|
||||
private val huggingFaceRemoteDataSource: HuggingFaceRemoteDataSource,
|
||||
private val ggufMetadataReader: GgufMetadataReader,
|
||||
) : ModelRepository {
|
||||
|
||||
private val modelsDir = File(context.filesDir, INTERNAL_STORAGE_PATH)
|
||||
|
|
@ -224,7 +225,7 @@ class ModelRepositoryImpl @Inject constructor(
|
|||
val metadata = try {
|
||||
val filePath = modelFile.absolutePath
|
||||
Log.i(TAG, "Extracting GGUF Metadata from $filePath")
|
||||
GgufMetadata.fromDomain(GgufMetadataReader().readStructuredMetadata(filePath))
|
||||
GgufMetadata.fromDomain(ggufMetadataReader.readStructuredMetadata(filePath))
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Cannot extract GGUF metadata: ${e.message}", e)
|
||||
throw e
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.example.llama.di
|
|||
import android.content.Context
|
||||
import android.llama.cpp.InferenceEngine
|
||||
import android.llama.cpp.InferenceEngineLoader
|
||||
import android.llama.cpp.gguf.GgufMetadataReader
|
||||
import com.example.llama.data.local.AppDatabase
|
||||
import com.example.llama.data.remote.HuggingFaceApiService
|
||||
import com.example.llama.data.remote.HuggingFaceRemoteDataSource
|
||||
|
|
@ -82,6 +83,10 @@ internal abstract class AppModule {
|
|||
@Provides
|
||||
fun providesSystemPromptDao(appDatabase: AppDatabase) = appDatabase.systemPromptDao()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesGgufMetadataReader(): GgufMetadataReader = GgufMetadataReader.create()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideOkhttpClient() = OkHttpClient.Builder()
|
||||
|
|
|
|||
|
|
@ -4,23 +4,62 @@ import java.io.File
|
|||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
|
||||
private val DEFAULT_SKIP_KEYS = setOf(
|
||||
"tokenizer.chat_template",
|
||||
"tokenizer.ggml.scores",
|
||||
"tokenizer.ggml.tokens",
|
||||
"tokenizer.ggml.token_type"
|
||||
)
|
||||
/**
|
||||
* Interface for reading GGUF metadata from model files.
|
||||
* Use `GgufMetadataReader.create()` to get an instance.
|
||||
*/
|
||||
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.
|
||||
* This parser reads the header and metadata of a GGUF v3 file (little-endian) and skips tensor data.
|
||||
*/
|
||||
class GgufMetadataReader(
|
||||
/** Keys whose value should be skipped entirely (not kept in the resulting map). */
|
||||
private val skipKeys: Set<String> = DEFAULT_SKIP_KEYS,
|
||||
/** If ≥0, arrays longer than this get summarised instead of materialised. -1 ⇒ never summarise. */
|
||||
private val arraySummariseThreshold: Int = 1_000
|
||||
) {
|
||||
private class GgufMetadataReaderImpl(
|
||||
private val skipKeys: Set<String>,
|
||||
private val arraySummariseThreshold: Int,
|
||||
) : GgufMetadataReader {
|
||||
companion object {
|
||||
private const val ARCH_LLAMA = "llama"
|
||||
}
|
||||
|
|
@ -92,7 +131,7 @@ class GgufMetadataReader(
|
|||
* @throws IOException if the file is not GGUF, the version is unsupported,
|
||||
* 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 ->
|
||||
// ── 1. header ──────────────────────────────────────────────────────────
|
||||
// throws on mismatch
|
||||
|
|
|
|||
Loading…
Reference in New Issue