createConversation method

void createConversation({
  1. String? systemMessage,
  2. String? toolsJson,
  3. double temperature = 0.8,
  4. int topK = 40,
  5. double? topP,
  6. int seed = 1,
})

Create a new conversation with optional system message and tools.

Implementation

void createConversation({
  String? systemMessage,
  String? toolsJson,
  double temperature = 0.8,
  int topK = 40,
  double? topP,
  int seed = 1,
}) {
  _assertInitialized();
  final b = _bindings!;

  // Close existing conversation if any
  if (_conversation != null && _conversation != nullptr) {
    b.litert_lm_conversation_delete(_conversation!);
    _conversation = null;
  }

  // Always build a sessionConfig with the caller's sampler params — even
  // when there's no systemMessage/tools. Otherwise temperature, topK,
  // topP, and seed get silently dropped on the floor and the model
  // falls back to its baked-in defaults (typically greedy), making
  // every call ignore stochastic decoding requests.
  //
  // This requires a patched libLiteRtLm.{so,dylib,dll} where
  // litert_lm_conversation_config_create accepts the 6-arg overload
  // and applies session_config via the upstream setter chain. See
  // native/litert_lm/patch_c_api.sh ("PATCH: 6-arg overload").
  final sessionConfig = b.litert_lm_session_config_create();

  final samplerParams = calloc<LiteRtLmSamplerParams>();
  // Upstream LiteRT-LM (commit 5e0d86b) only implements TopP sampling at
  // engine level — sampler type 1 (TopK) and 3 (Greedy) are rejected with
  // "UNIMPLEMENTED: Sampler type: N not implemented yet." Use TopP (=2)
  // unconditionally and pass top_k as a hint; native respects both fields
  // even though it's gated by the type tag.
  samplerParams.ref.typeAsInt = 2; // always TopP
  samplerParams.ref.top_k = topK;
  samplerParams.ref.top_p = topP ?? 0.95;
  samplerParams.ref.temperature = temperature;
  samplerParams.ref.seed = seed;
  b.litert_lm_session_config_set_sampler_params(sessionConfig, samplerParams);
  calloc.free(samplerParams);

  final systemPtr = systemMessage?.toNativeUtf8();
  final toolsPtr = toolsJson?.toNativeUtf8();

  final Pointer<LiteRtLmConversationConfig> convConfig =
      b.litert_lm_conversation_config_create(
    _engine!,
    sessionConfig,
    systemPtr?.cast() ?? nullptr,
    toolsPtr?.cast() ?? nullptr,
    nullptr,
    toolsJson != null,
  );

  b.litert_lm_session_config_delete(sessionConfig);
  if (systemPtr != null) calloc.free(systemPtr);
  if (toolsPtr != null) calloc.free(toolsPtr);

  if (convConfig == nullptr) {
    throw Exception(
      'litert_lm_conversation_config_create returned null '
      '(systemMessage=${systemMessage != null}, tools=${toolsJson != null}, '
      'temperature=$temperature, topK=$topK, topP=$topP)',
    );
  }

  _conversation = b.litert_lm_conversation_create(_engine!, convConfig);

  b.litert_lm_conversation_config_delete(convConfig);

  if (_conversation == null || _conversation == nullptr) {
    _dumpNativeLog();
    throw Exception('Failed to create conversation');
  }

  debugPrint('[LiteRtLmFfi] Conversation created');
}