llama-cpp-capacitor 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,4 +17,7 @@ Pod::Spec.new do |s|
17
17
 
18
18
  # Include the native llama-cpp framework
19
19
  s.vendored_frameworks = 'ios/Frameworks/llama-cpp.framework'
20
+ s.pod_target_xcconfig = {
21
+ 'FRAMEWORK_SEARCH_PATHS' => '$(inherited) "$(PODS_TARGET_SRCROOT)/ios/Frameworks"'
22
+ }
20
23
  end
package/build-native.sh CHANGED
@@ -93,36 +93,53 @@ build_ios() {
93
93
  cd ios/build
94
94
 
95
95
  # Configure with CMake
96
+ # IMPORTANT: CMAKE_OSX_SYSROOT=iphoneos ensures we build for iOS, not macOS.
97
+ # Without it, the framework would be built for macOS and fail to link in an iOS app.
96
98
  # IMPORTANT: build iOS framework as **ARM64-only**.
97
99
  # Including x86_64 here makes CMake/Xcode try to link an x86_64 slice,
98
100
  # but we only compile ARM-specific kernels (arch/arm), which leads to
99
101
  # undefined symbols like lm_ggml_gemm_* for x86_64.
100
102
  cmake .. \
101
103
  -DCMAKE_BUILD_TYPE=Release \
104
+ -DCMAKE_OSX_SYSROOT=iphoneos \
102
105
  -DCMAKE_OSX_ARCHITECTURES="arm64" \
103
106
  -DCMAKE_OSX_DEPLOYMENT_TARGET=13.0 \
104
107
  -DCMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE=NO
105
108
 
106
- # Build
107
- cmake --build . --config Release
109
+ # Build (parallel: -j uses available CPU cores)
110
+ JOBS=$(sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || echo 4)
111
+ cmake --build . --config Release -- -j"$JOBS"
108
112
 
109
113
  # CMake builds the framework directly (FRAMEWORK TRUE in CMakeLists.txt)
110
114
  # Verify the framework was created
111
115
  if [ -d "llama-cpp.framework" ]; then
112
116
  print_success "iOS framework built successfully at: $(pwd)/llama-cpp.framework"
113
117
 
114
- # Strip debug symbols to reduce app store size (~0.5–1 MB)
115
- BINARY="llama-cpp.framework/Versions/A/llama-cpp"
116
- if [ -f "$BINARY" ]; then
118
+ # Binary location: CMake may produce flat (llama-cpp) or Versions/A/ layout
119
+ BINARY=""
120
+ if [ -f "llama-cpp.framework/llama-cpp" ]; then
121
+ BINARY="llama-cpp.framework/llama-cpp"
122
+ elif [ -f "llama-cpp.framework/Versions/A/llama-cpp" ]; then
123
+ BINARY="llama-cpp.framework/Versions/A/llama-cpp"
124
+ fi
125
+ if [ -n "$BINARY" ]; then
117
126
  if xcrun strip -x -S "$BINARY" 2>/dev/null; then
118
127
  print_status "Stripped debug symbols from iOS framework"
119
128
  fi
120
129
  fi
121
130
 
122
- # Copy framework to package location for npm publishing
123
- mkdir -p ../Frameworks
124
- cp -R llama-cpp.framework ../Frameworks/
125
- print_success "iOS framework copied to ios/Frameworks/ for npm package"
131
+ # Build flat framework (single binary, no duplication) for npm publishing
132
+ if [ -z "$BINARY" ] || [ ! -f "$BINARY" ]; then
133
+ print_error "iOS framework binary not found"
134
+ cd ../..
135
+ return 1
136
+ fi
137
+ rm -rf ../Frameworks/llama-cpp.framework
138
+ mkdir -p ../Frameworks/llama-cpp.framework/Resources
139
+ cp "$BINARY" ../Frameworks/llama-cpp.framework/llama-cpp
140
+ [ -f llama-cpp.framework/Info.plist ] && cp llama-cpp.framework/Info.plist ../Frameworks/llama-cpp.framework/
141
+ [ -f llama-cpp.framework/Versions/A/Resources/Info.plist ] && cp llama-cpp.framework/Versions/A/Resources/Info.plist ../Frameworks/llama-cpp.framework/Resources/
142
+ print_success "iOS framework copied to ios/Frameworks/ for npm package (flat, no duplication)"
126
143
  else
127
144
  print_error "iOS framework not found after build"
128
145
  cd ../..
@@ -173,7 +190,8 @@ build_android() {
173
190
  -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN_FILE" \
174
191
  -DANDROID_STL=c++_shared
175
192
 
176
- cmake --build . --config Release
193
+ JOBS=$(sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || echo 4)
194
+ cmake --build . --config Release -- -j"$JOBS"
177
195
 
178
196
  mkdir -p ../src/main/jniLibs/$arch
179
197
  if [ -f "libllama-cpp-arm64.so" ]; then
@@ -201,6 +219,10 @@ build_android() {
201
219
  main() {
202
220
  print_status "Starting llama-cpp Capacitor plugin build..."
203
221
 
222
+ # Always start clean - remove previous build outputs
223
+ rm -rf ios/build ios/Frameworks android/build
224
+ print_status "Cleaned build directories"
225
+
204
226
  # Check dependencies
205
227
  if ! command -v cmake &> /dev/null; then
206
228
  print_error "CMake is required but not installed"
@@ -225,5 +247,5 @@ main() {
225
247
  print_success "Build completed successfully!"
226
248
  }
227
249
 
228
- # Run main function
229
- main "$@"
250
+ # Run main function (ignore any args npm may pass)
251
+ main
@@ -150,6 +150,8 @@ struct MinjaCaps {
150
150
  // MARK: - Main Implementation
151
151
  @objc public class LlamaCpp: NSObject {
152
152
  private var contexts: [Int: LlamaContext] = [:]
153
+ private var nativeContexts: [Int64: UnsafeMutableRawPointer] = [:]
154
+ private var contextIdToNative: [Int: Int64] = [:]
153
155
  private var contextCounter: Int = 0
154
156
  private var contextLimit: Int = 10
155
157
  private var nativeLogEnabled: Bool = false
@@ -262,18 +264,16 @@ struct MinjaCaps {
262
264
 
263
265
  let nativeContextId = initFunc(modelPath, paramsJson.cString(using: .utf8)!)
264
266
  if nativeContextId > 0 {
265
- // Store the native context pointer (the ID is used as the pointer value)
266
- // Note: In a real implementation, the native function would return the actual pointer
267
- // For now, we use the contextId as the pointer identifier
268
- contexts[Int64(contextId)] = UnsafeMutableRawPointer(bitPattern: Int(nativeContextId))
267
+ // Store the LlamaContext for Swift bookkeeping
268
+ contexts[contextId] = context
269
+ // Store the native context pointer and mapping for C layer
270
+ let nativePtr = UnsafeMutableRawPointer(bitPattern: Int(nativeContextId))
271
+ nativeContexts[nativeContextId] = nativePtr
272
+ contextIdToNative[contextId] = nativeContextId
269
273
 
270
274
  // Register with embedding system if available
271
- // The C layer needs the actual llama_cap_context pointer, which should come from the native init
272
- // For now, we'll register with the contextId - the C layer will need to look it up
273
- if let registerFunc = registerEmbeddingContextFunc {
274
- // Note: The actual context pointer should come from the native initContext function
275
- // This is a placeholder - the real implementation needs to get the actual pointer
276
- registerFunc(Int64(contextId), contexts[Int64(contextId)]!)
275
+ if let registerFunc = registerEmbeddingContextFunc, let ptr = nativePtr {
276
+ registerFunc(nativeContextId, ptr)
277
277
  }
278
278
  } else {
279
279
  completion(.failure(.operationFailed("Failed to initialize native context")))
@@ -322,34 +322,40 @@ struct MinjaCaps {
322
322
  }
323
323
 
324
324
  func releaseContext(contextId: Int, completion: @escaping (LlamaResult<Void>) -> Void) {
325
- guard let contextPtr = contexts[contextId] else {
325
+ guard contexts[contextId] != nil else {
326
326
  completion(.failure(.contextNotFound))
327
327
  return
328
328
  }
329
329
 
330
+ let nativeId = contextIdToNative[contextId] ?? Int64(contextId)
331
+
330
332
  // Unregister from embedding system if available
331
333
  if let unregisterFunc = unregisterEmbeddingContextFunc {
332
- unregisterFunc(Int64(contextId))
334
+ unregisterFunc(nativeId)
333
335
  }
334
336
 
335
337
  // Call native release function
336
338
  if let releaseFunc = releaseContextFunc {
337
- releaseFunc(Int64(contextId))
339
+ releaseFunc(nativeId)
338
340
  }
339
341
 
340
342
  contexts.removeValue(forKey: contextId)
343
+ nativeContexts.removeValue(forKey: nativeId)
344
+ contextIdToNative.removeValue(forKey: contextId)
341
345
  completion(.success(()))
342
346
  }
343
347
 
344
348
  func releaseAllContexts(completion: @escaping (LlamaResult<Void>) -> Void) {
345
349
  // Unregister all contexts from embedding system
346
350
  if let unregisterFunc = unregisterEmbeddingContextFunc {
347
- for contextId in contexts.keys {
348
- unregisterFunc(Int64(contextId))
351
+ for (_, nativeId) in contextIdToNative {
352
+ unregisterFunc(nativeId)
349
353
  }
350
354
  }
351
355
 
352
356
  contexts.removeAll()
357
+ nativeContexts.removeAll()
358
+ contextIdToNative.removeAll()
353
359
  completion(.success(()))
354
360
  }
355
361
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llama-cpp-capacitor",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "A native Capacitor plugin that embeds llama.cpp directly into mobile apps, enabling offline AI inference with chat-first API design. Complete iOS and Android support: text generation, chat, multimodal, TTS, LoRA, embeddings, and more.",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "type": "module",
@@ -72,6 +72,7 @@
72
72
  "build:all": "npm run build && npm run build:native",
73
73
  "build:ios": "cd ios && cmake -B build -S . && cmake --build build --config Release",
74
74
  "build:android": "cd android && gradlew.bat assembleRelease",
75
+ "prepack": "npm run build:native",
75
76
  "pack": "npm run build && npm pack --dry-run",
76
77
  "pack:full": "npm run build:all && npm pack --dry-run",
77
78
  "test": "jest",
@@ -84,7 +85,7 @@
84
85
  "clean:native": "rimraf ios/build ios/Frameworks android/build android/src/main/jniLibs",
85
86
  "clean:test": "rimraf test/output test/coverage",
86
87
  "watch": "tsc --watch",
87
- "prepublishOnly": "npm run build"
88
+ "prepublishOnly": "npm run build && npm run build:native"
88
89
  },
89
90
  "devDependencies": {
90
91
  "@capacitor/android": "^7.0.0",