Skip to content

Add cross-provider exportConversation/importConversation to all LLM providers#1788

Merged
nmaguiar merged 14 commits intot8from
copilot/add-export-import-conversation-methods
Mar 27, 2026
Merged

Add cross-provider exportConversation/importConversation to all LLM providers#1788
nmaguiar merged 14 commits intot8from
copilot/add-export-import-conversation-methods

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 26, 2026

Each provider stores conversations in an incompatible internal format — Gemini uses parts/functionCall/functionResponse, OpenAI uses tool_calls/role:"tool", Anthropic uses typed content blocks — making cross-provider conversation transfer lossy or broken for any conversation involving tool calls.

Portable Format

A provider-neutral JSON-serialisable format normalised across all providers:

[
  { "role": "system"|"user"|"assistant", "content": "string|null" },
  { "role": "assistant", "content": null,
    "toolCalls": [{ "id": "...", "name": "fn", "arguments": { "parsed": "object" } }] },
  { "role": "user", "content": null,
    "toolResults": [{ "id": "...", "name": "fn", "result": "string|object" }] }
]

Rules: role is always one of system/user/assistant; arguments is always a parsed object (never a JSON string); toolCalls only on assistant turns; toolResults only on user turns.

Per-Provider Changes

  • OpenAI — export: normalises developersystem, batches consecutive role:"tool" entries into a single toolResults user turn, parses tool_calls[].function.arguments from string to object. Import: reconstructs tool_calls array with synthetic IDs when absent.
  • Gemini — export: role:"model""assistant", maps functionCall parts→toolCalls, functionResponse parts→toolResults (preserving structured response content). Import: rebuilds native parts:[{functionCall}] / parts:[{functionResponse}] entries per tool result.
  • Ollama — export/import: mirrors OpenAI logic; handles tc.function.arguments being either string or object.
  • Anthropic — export: pre-builds id→name map from tool_use blocks for name lookup in tool_result entries; maps typed content blocks to portable fields. Import: reconstructs tool_use/tool_result content block arrays.

API Surface

// ow.ai.gpt instance
var exported = gpt.exportConversation()   // → portable array
gptB.importConversation(exported)         // load into any provider

// $gpt wrapper
var conv = $gpt({ type: "openai", ... }).exportConversation()
$gpt({ type: "gemini", ... }).importConversation(conv).prompt("continue…")

Both methods are exposed on ow.ai.gpt.prototype and forwarded through the $gpt wrapper (with odoc documentation). Existing getConversation/setConversation behaviour is unchanged.

Original prompt

Summary

Add exportConversation() and importConversation(aExport) methods to all LLM provider implementations in js/owrap.ai.js, and expose them as standard methods on ow.ai.gpt prototype and on the $gpt/$llm wrappers.

The goal is to allow a conversation saved from one provider (e.g. OpenAI) to be safely loaded into another provider (e.g. Gemini, Ollama, Anthropic), using a common portable format that preserves all message types including text turns and tool/function call turns.


Background & Problem

Each provider currently stores its conversation array in a provider-specific internal format:

  • OpenAI/Anthropic: { role, content } — but content for tool calls is a complex array with tool_calls, tool_use, tool_result, etc.
  • Gemini: { role: "model"|"user", parts: [{text}] } — roles differ ("model" vs "assistant"), and tool calls use functionCall/functionResponse parts.
  • Ollama: { role, content } — tool calls use tool_calls in message content.

getConversation() on Gemini already does partial normalization (converts "model""assistant", flattens parts to content), but drops non-text parts (tool call/response parts), making round-trip export/import lossy for tool conversations.

There is no existing cross-provider export/import facility. Calling setConversation(providerA.getConversation()) on provider B is unsafe because:

  1. Role names differ ("model" vs "assistant", "developer" vs "system").
  2. Content structures differ (Gemini uses parts, others use content).
  3. Tool interaction turns (function calls, function responses, tool results) use completely incompatible schemas between providers.

Requirements

1. Standard Portable Conversation Format

Define a standard portable format as a JSON-serialisable array of message objects. Each entry must carry:

[
  {
    "role": "system" | "user" | "assistant",
    "content": "<string or null>",
    "toolCalls": [                        // optional – present when the assistant requested tool calls
      {
        "id": "<string>",                 // tool call id (may be empty string for providers that don't track ids)
        "name": "<string>",               // tool/function name
        "arguments": { ... }              // parsed JSON arguments object
      }
    ],
    "toolResults": [                      // optional – present on the turn that returns tool results
      {
        "id": "<string>",                 // tool call id (matching a previous toolCalls entry)
        "name": "<string>",               // tool/function name
        "result": "<string or object>"    // tool result content
      }
    ]
  }
]

Rules:

  • role must always be one of "system", "user", "assistant" (never "model", "developer", "tool").
  • content is a plain string (or null when the turn is purely a tool call/response).
  • toolCalls is present only on assistant turns that invoked tools.
  • toolResults is present only on user turns that carry tool results.
  • The format must be round-trippable: exporting from provider A and importing into provider B must produce a semantically equivalent conversation (tool turns included).

2. Per-Provider exportConversation() Method

Add exportConversation() to the internal _r object inside each provider's create() factory (openai, gemini, ollama, anthropic).

Each implementation must convert from the provider's native internal format to the standard portable format above.

OpenAI export logic

  • Plain text turns: { role, content: string } → straightforward.
  • Assistant tool-call turns: { role: "assistant", tool_calls: [...] } → emit toolCalls array; content may be null.
  • Tool result turns: { role: "tool", content, tool_call_id } → emit as role: "user" with toolResults.
  • Merge consecutive "tool" role messages into a single user+toolResults entry where possible.
  • Normalize "developer" role → "system".

Gemini export logic

  • Convert role: "model""assistant".
  • For entries with parts: extract text parts into content; extract functionCall parts into toolCalls; extract functionResponse parts into toolResults (role becomes "user").
  • System instruction entries: role: "system".

Ollama export logic

  • Similar to OpenAI: assistant tool_callstoolCalls; role: "tool" entries → toolResults on a user turn.

Anthropic export logic

  • Content array entries of type "text"content string.
  • Content array entries of type "tool_use"toolCalls.
  • Content array entries of type "tool_result" on user turns → toolResults.

3. Per-Provider importConversation(aExport) Method

Add importConversation(aExport) to the internal _r object inside each provider's create() factory.

Each implementation receives the standard portable format and **converts it into the provider's native inte...

This pull request was created from Copilot chat.


💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.

nmaguiar and others added 13 commits February 16, 2026 10:21
refactor: update documentation for getK8sRemoteURLArrayFunc
Bumps [com.fasterxml.jackson.core:jackson-core](https://github.com/FasterXML/jackson-core) from 2.21.0 to 2.21.1.
- [Commits](FasterXML/jackson-core@jackson-core-2.21.0...jackson-core-2.21.1)

---
updated-dependencies:
- dependency-name: com.fasterxml.jackson.core:jackson-core
  dependency-version: 2.21.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
…ackson.core-jackson-core-2.21.1

chore(deps): bump com.fasterxml.jackson.core:jackson-core from 2.21.0 to 2.21.1
chore(deps): update commons-logging to version 1.3.6 and jetty-compre…
chore(deps): upgrade Jetty and Okio dependencies to latest versions
@nmaguiar nmaguiar changed the base branch from master to t8 March 27, 2026 00:03
…pt wrapper

Agent-Logs-Url: https://github.com/OpenAF/openaf/sessions/6dc098b3-008a-41a0-ae8e-0caf57201a9f

Co-authored-by: nmaguiar <11761746+nmaguiar@users.noreply.github.com>
Copilot AI changed the title [WIP] Add exportConversation and importConversation methods to LLM providers Add cross-provider exportConversation/importConversation to all LLM providers Mar 27, 2026
Copilot AI requested a review from nmaguiar March 27, 2026 00:12
@nmaguiar nmaguiar marked this pull request as ready for review March 27, 2026 02:12
@nmaguiar nmaguiar merged commit c84fd1a into t8 Mar 27, 2026
1 of 2 checks passed
@nmaguiar nmaguiar deleted the copilot/add-export-import-conversation-methods branch March 27, 2026 02:12
@nmaguiar nmaguiar restored the copilot/add-export-import-conversation-methods branch March 27, 2026 03:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants