From abd721c0a5fad3b000d444061f778c5e3d0baabf Mon Sep 17 00:00:00 2001 From: chmjkb Date: Tue, 3 Feb 2026 14:13:46 +0100 Subject: [PATCH 1/3] feat: add getTotalTokenCount, getPromptTokenCount --- .../common/rnexecutorch/models/llm/LLM.cpp | 7 +++++++ .../common/rnexecutorch/models/llm/LLM.h | 1 + .../src/controllers/LLMController.ts | 14 ++++++++++++++ .../hooks/natural_language_processing/useLLM.ts | 12 ++++++++++++ .../natural_language_processing/LLMModule.ts | 8 ++++++++ packages/react-native-executorch/src/types/llm.ts | 2 ++ 6 files changed, 44 insertions(+) diff --git a/packages/react-native-executorch/common/rnexecutorch/models/llm/LLM.cpp b/packages/react-native-executorch/common/rnexecutorch/models/llm/LLM.cpp index 9d4f9feb0..c2b4c831f 100644 --- a/packages/react-native-executorch/common/rnexecutorch/models/llm/LLM.cpp +++ b/packages/react-native-executorch/common/rnexecutorch/models/llm/LLM.cpp @@ -71,6 +71,13 @@ size_t LLM::getGeneratedTokenCount() const noexcept { return runner->stats_.num_generated_tokens; } +size_t LLM::getPromptTokenCount() const noexcept { + if (!runner || !runner->is_loaded()) { + return 0; + } + return runner->stats_.num_prompt_tokens; +} + size_t LLM::getMemoryLowerBound() const noexcept { return memorySizeLowerBound; } diff --git a/packages/react-native-executorch/common/rnexecutorch/models/llm/LLM.h b/packages/react-native-executorch/common/rnexecutorch/models/llm/LLM.h index 6b778f56c..b23c9677a 100644 --- a/packages/react-native-executorch/common/rnexecutorch/models/llm/LLM.h +++ b/packages/react-native-executorch/common/rnexecutorch/models/llm/LLM.h @@ -23,6 +23,7 @@ class LLM : public BaseModel { void interrupt(); void unload() noexcept; size_t getGeneratedTokenCount() const noexcept; + size_t getPromptTokenCount() const noexcept; size_t getMemoryLowerBound() const noexcept; void setCountInterval(size_t countInterval); void setTemperature(float temperature); diff --git a/packages/react-native-executorch/src/controllers/LLMController.ts b/packages/react-native-executorch/src/controllers/LLMController.ts index 5c768aab4..46e32d8ae 100644 --- a/packages/react-native-executorch/src/controllers/LLMController.ts +++ b/packages/react-native-executorch/src/controllers/LLMController.ts @@ -255,6 +255,20 @@ export class LLMController { return this.nativeModule.getGeneratedTokenCount(); } + public getPromptTokensCount(): number { + if (!this.nativeModule) { + throw new RnExecutorchError( + RnExecutorchErrorCode.ModuleNotLoaded, + "Cannot get prompt token count for a model that's not loaded." + ); + } + return this.nativeModule.getPromptTokensCount(); + } + + public getTotalTokensCount(): number { + return this.getGeneratedTokenCount() + this.getPromptTokensCount(); + } + public async generate( messages: Message[], tools?: LLMTool[] diff --git a/packages/react-native-executorch/src/hooks/natural_language_processing/useLLM.ts b/packages/react-native-executorch/src/hooks/natural_language_processing/useLLM.ts index 852441556..61a0e0e98 100644 --- a/packages/react-native-executorch/src/hooks/natural_language_processing/useLLM.ts +++ b/packages/react-native-executorch/src/hooks/natural_language_processing/useLLM.ts @@ -130,6 +130,16 @@ export const useLLM = ({ [controllerInstance] ); + const getPromptTokenCount = useCallback( + () => controllerInstance.getPromptTokensCount(), + [controllerInstance] + ); + + const getTotalTokenCount = useCallback( + () => controllerInstance.getTotalTokensCount(), + [controllerInstance] + ); + return { messageHistory, response, @@ -139,6 +149,8 @@ export const useLLM = ({ downloadProgress, error, getGeneratedTokenCount: getGeneratedTokenCount, + getPromptTokenCount: getPromptTokenCount, + getTotalTokenCount: getTotalTokenCount, configure: configure, generate: generate, sendMessage: sendMessage, diff --git a/packages/react-native-executorch/src/modules/natural_language_processing/LLMModule.ts b/packages/react-native-executorch/src/modules/natural_language_processing/LLMModule.ts index b00aac29c..0fe57fac9 100644 --- a/packages/react-native-executorch/src/modules/natural_language_processing/LLMModule.ts +++ b/packages/react-native-executorch/src/modules/natural_language_processing/LLMModule.ts @@ -85,6 +85,14 @@ export class LLMModule { return this.controller.getGeneratedTokenCount(); } + getPromptTokensCount() { + return this.controller.getPromptTokensCount(); + } + + getTotalTokensCount() { + return this.controller.getTotalTokensCount(); + } + delete() { this.controller.delete(); } diff --git a/packages/react-native-executorch/src/types/llm.ts b/packages/react-native-executorch/src/types/llm.ts index c2ecf69aa..9d7d39cc2 100644 --- a/packages/react-native-executorch/src/types/llm.ts +++ b/packages/react-native-executorch/src/types/llm.ts @@ -18,6 +18,8 @@ export interface LLMType { generationConfig?: GenerationConfig; }) => void; getGeneratedTokenCount: () => number; + getTotalTokenCount: () => number; + getPromptTokenCount: () => number; generate: (messages: Message[], tools?: LLMTool[]) => Promise; sendMessage: (message: string) => Promise; deleteMessage: (index: number) => void; From 8fa97f4cc3df21c5f0b3f8044654262e64ba6c27 Mon Sep 17 00:00:00 2001 From: chmjkb Date: Tue, 3 Feb 2026 14:25:08 +0100 Subject: [PATCH 2/3] fix: add docs, expose method from cpp --- apps/llm/app/llm/index.tsx | 3 +++ .../03-hooks/01-natural-language-processing/useLLM.md | 4 ++++ .../01-natural-language-processing/LLMModule.md | 2 ++ .../common/rnexecutorch/host_objects/ModelHostObject.h | 5 +++++ .../src/controllers/LLMController.ts | 8 ++++---- .../src/hooks/natural_language_processing/useLLM.ts | 4 ++-- .../src/modules/natural_language_processing/LLMModule.ts | 4 ++-- 7 files changed, 22 insertions(+), 8 deletions(-) diff --git a/apps/llm/app/llm/index.tsx b/apps/llm/app/llm/index.tsx index e6ee183ee..2abba46ff 100644 --- a/apps/llm/app/llm/index.tsx +++ b/apps/llm/app/llm/index.tsx @@ -47,6 +47,9 @@ function LLMScreen() { textInputRef.current?.clear(); try { await llm.sendMessage(userInput); + console.log(llm.getGeneratedTokenCount()); + console.log(llm.getPromptTokenCount()); + console.log(llm.getTotalTokenCount()); } catch (e) { console.error(e); } diff --git a/docs/docs/03-hooks/01-natural-language-processing/useLLM.md b/docs/docs/03-hooks/01-natural-language-processing/useLLM.md index 56deff82a..98dd26aed 100644 --- a/docs/docs/03-hooks/01-natural-language-processing/useLLM.md +++ b/docs/docs/03-hooks/01-natural-language-processing/useLLM.md @@ -75,6 +75,8 @@ For more information on loading resources, take a look at [loading models](../.. | `deleteMessage` | `(index: number) => void` | Deletes all messages starting with message on `index` position. After deletion `messageHistory` will be updated. | | `messageHistory` | `Message[]` | History containing all messages in conversation. This field is updated after model responds to `sendMessage`. | | `getGeneratedTokenCount` | `() => number` | Returns the number of tokens generated in the last response. | +| `getPromptTokenCount` | `() => number` | Returns the number of tokens in the prompt (input tokens) for the current session. | +| `getTotalTokenCount` | `() => number` | Returns the total number of tokens processed (sum of prompt tokens and generated tokens). |
Type definitions @@ -110,6 +112,8 @@ interface LLMType { generationConfig?: GenerationConfig; }) => void; getGeneratedTokenCount: () => number; + getPromptTokenCount: () => number; + getTotalTokenCount: () => number; generate: (messages: Message[], tools?: LLMTool[]) => Promise; sendMessage: (message: string) => Promise; deleteMessage: (index: number) => void; diff --git a/docs/docs/04-typescript-api/01-natural-language-processing/LLMModule.md b/docs/docs/04-typescript-api/01-natural-language-processing/LLMModule.md index a2d3f4e8c..be92bc4cd 100644 --- a/docs/docs/04-typescript-api/01-natural-language-processing/LLMModule.md +++ b/docs/docs/04-typescript-api/01-natural-language-processing/LLMModule.md @@ -44,6 +44,8 @@ llm.delete(); | `delete` | `() => void` | Method to delete the model from memory. Note you cannot delete model while it's generating. You need to interrupt it first and make sure model stopped generation. | | `interrupt` | `() => void` | Interrupts model generation. It may return one more token after interrupt. | | `getGeneratedTokenCount` | `() => number` | Returns the number of tokens generated in the last response. | +| `getPromptTokenCount` | `() => number` | Returns the number of tokens in the prompt (input tokens) for the current session. | +| `getTotalTokenCount` | `() => number` | Returns the total number of tokens processed (sum of prompt tokens and generated tokens). |
Type definitions diff --git a/packages/react-native-executorch/common/rnexecutorch/host_objects/ModelHostObject.h b/packages/react-native-executorch/common/rnexecutorch/host_objects/ModelHostObject.h index 9f7cb08ea..a1ce8e8e8 100644 --- a/packages/react-native-executorch/common/rnexecutorch/host_objects/ModelHostObject.h +++ b/packages/react-native-executorch/common/rnexecutorch/host_objects/ModelHostObject.h @@ -107,6 +107,11 @@ template class ModelHostObject : public JsiHostObject { synchronousHostFunction<&Model::getGeneratedTokenCount>, "getGeneratedTokenCount")); + addFunctions(JSI_EXPORT_FUNCTION( + ModelHostObject, + synchronousHostFunction<&Model::getPromptTokenCount>, + "getPromptTokenCount")); + addFunctions( JSI_EXPORT_FUNCTION(ModelHostObject, synchronousHostFunction<&Model::setCountInterval>, diff --git a/packages/react-native-executorch/src/controllers/LLMController.ts b/packages/react-native-executorch/src/controllers/LLMController.ts index 46e32d8ae..06ae59100 100644 --- a/packages/react-native-executorch/src/controllers/LLMController.ts +++ b/packages/react-native-executorch/src/controllers/LLMController.ts @@ -255,18 +255,18 @@ export class LLMController { return this.nativeModule.getGeneratedTokenCount(); } - public getPromptTokensCount(): number { + public getPromptTokenCount(): number { if (!this.nativeModule) { throw new RnExecutorchError( RnExecutorchErrorCode.ModuleNotLoaded, "Cannot get prompt token count for a model that's not loaded." ); } - return this.nativeModule.getPromptTokensCount(); + return this.nativeModule.getPromptTokenCount(); } - public getTotalTokensCount(): number { - return this.getGeneratedTokenCount() + this.getPromptTokensCount(); + public getTotalTokenCount(): number { + return this.getGeneratedTokenCount() + this.getPromptTokenCount(); } public async generate( diff --git a/packages/react-native-executorch/src/hooks/natural_language_processing/useLLM.ts b/packages/react-native-executorch/src/hooks/natural_language_processing/useLLM.ts index 61a0e0e98..d8f3d80ca 100644 --- a/packages/react-native-executorch/src/hooks/natural_language_processing/useLLM.ts +++ b/packages/react-native-executorch/src/hooks/natural_language_processing/useLLM.ts @@ -131,12 +131,12 @@ export const useLLM = ({ ); const getPromptTokenCount = useCallback( - () => controllerInstance.getPromptTokensCount(), + () => controllerInstance.getPromptTokenCount(), [controllerInstance] ); const getTotalTokenCount = useCallback( - () => controllerInstance.getTotalTokensCount(), + () => controllerInstance.getTotalTokenCount(), [controllerInstance] ); diff --git a/packages/react-native-executorch/src/modules/natural_language_processing/LLMModule.ts b/packages/react-native-executorch/src/modules/natural_language_processing/LLMModule.ts index 0fe57fac9..bbb4c6d36 100644 --- a/packages/react-native-executorch/src/modules/natural_language_processing/LLMModule.ts +++ b/packages/react-native-executorch/src/modules/natural_language_processing/LLMModule.ts @@ -86,11 +86,11 @@ export class LLMModule { } getPromptTokensCount() { - return this.controller.getPromptTokensCount(); + return this.controller.getPromptTokenCount(); } getTotalTokensCount() { - return this.controller.getTotalTokensCount(); + return this.controller.getTotalTokenCount(); } delete() { From bbc536a707051c6981b78132b1c2821659c3ec8c Mon Sep 17 00:00:00 2001 From: chmjkb Date: Tue, 3 Feb 2026 14:27:13 +0100 Subject: [PATCH 3/3] chore: remove console.logs --- apps/llm/app/llm/index.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/llm/app/llm/index.tsx b/apps/llm/app/llm/index.tsx index 2abba46ff..e6ee183ee 100644 --- a/apps/llm/app/llm/index.tsx +++ b/apps/llm/app/llm/index.tsx @@ -47,9 +47,6 @@ function LLMScreen() { textInputRef.current?.clear(); try { await llm.sendMessage(userInput); - console.log(llm.getGeneratedTokenCount()); - console.log(llm.getPromptTokenCount()); - console.log(llm.getTotalTokenCount()); } catch (e) { console.error(e); }