Cara Menampilkan Kembali History Chat Codex Desktop Setelah Pindah ke 9Router, Saat mencoba memakai 9Router di Codex Desktop / Codex App, saya menemukan satu masalah: setelah config 9Router diterapkan, history chat lama yang dibuat saat login ChatGPT/OpenAI terlihat hilang.
Awalnya saya kira chat lama benar-benar terhapus. Ternyata tidak. Chat tersebut masih ada di komputer, hanya saja Codex menampilkan history berdasarkan model_provider yang sedang aktif.
Masalahnya
Sebelum memakai 9Router, config Codex saya tidak memiliki baris ini:
model_provider = "9router"
Setelah apply config dari 9Router, config berubah menjadi seperti ini:
model = "conding-combo"
model_provider = "9router"
[model_providers.9router]
name = "9Router"
base_url = "http://127.0.0.1:20128/v1"
wire_api = "responses"
Efeknya:
Saat provider OpenAI aktif:
- chat OpenAI muncul
- chat 9Router tidak muncul
Saat provider 9Router aktif:
- chat 9Router muncul
- chat OpenAI tidak muncul
Jadi sebenarnya chat tidak hilang, tetapi terpisah berdasarkan provider.
Lokasi Data Codex
Di macOS, data Codex ada di:
~/.codex
Database history-nya ada di:
~/.codex/state_5.sqlite
Di database tersebut ada tabel threads, dan salah satu kolom pentingnya adalah:
model_provider
Pada kasus saya, hasil pengecekan menunjukkan:
TABLE: threads
'openai': 31
'9router': 3
Artinya ada 31 chat lama yang masih berlabel openai, dan 3 chat baru yang berlabel 9router.
Tujuan
Karena saya ingin menggunakan 9Router sebagai provider utama, maka saya mengubah label history lama dari:
openai
menjadi:
9router
Supaya semua chat tampil ketika Codex memakai config 9Router.
Peringatan
Cara ini termasuk hack lokal. Lakukan dengan hati-hati.
Sebelum mengubah apa pun, tutup Codex terlebih dahulu dan backup folder .codex.
osascript -e 'quit app "Codex"'
pkill -f Codex
pkill -f codex
Backup folder Codex:
cp -a ~/.codex ~/.codex.backup-before-provider-merge-$(date +%Y%m%d-%H%M%S)
Jika muncul pesan seperti ini:
fsmonitor--daemon.ipc is a socket (not copied)
itu aman diabaikan. File tersebut adalah socket/runtime, bukan data chat utama.
Cek Isi Database
Jalankan perintah ini untuk melihat provider yang tersimpan di database:
python3 - <<'PY'
import sqlite3
from pathlib import Path
db = Path.home() / ".codex" / "state_5.sqlite"
print("DB:", db)
print("exists:", db.exists())
conn = sqlite3.connect(db)
cur = conn.cursor()
cur.execute("SELECT name FROM sqlite_master WHERE type='table'")
tables = [r[0] for r in cur.fetchall()]
for table in tables:
cur.execute(f"PRAGMA table_info({table})")
cols = [r[1] for r in cur.fetchall()]
if "model_provider" in cols:
print("\nTABLE:", table)
cur.execute(f"""
SELECT model_provider, COUNT(*)
FROM {table}
GROUP BY model_provider
ORDER BY COUNT(*) DESC
""")
for provider, count in cur.fetchall():
print(f" {provider!r}: {count}")
conn.close()
PY
Contoh hasil:
TABLE: threads
'openai': 31
'9router': 3
Patch Provider dari OpenAI ke 9Router
Setelah yakin datanya benar, jalankan script berikut:
python3 - <<'PY'
import sqlite3
import shutil
from pathlib import Path
from datetime import datetime
codex = Path.home() / ".codex"
db = codex / "state_5.sqlite"
backup_dir = codex / f"provider-force-merge-{datetime.now().strftime('%Y%m%d-%H%M%S')}"
backup_dir.mkdir(parents=True, exist_ok=True)
backup_db = backup_dir / "state_5.sqlite.bak"
shutil.copy2(db, backup_db)
print("Backup DB:", backup_db)
conn = sqlite3.connect(db)
cur = conn.cursor()
print("\nSebelum:")
cur.execute("""
SELECT model_provider, archived, thread_source, cwd, COUNT(*)
FROM threads
GROUP BY model_provider, archived, thread_source, cwd
ORDER BY model_provider, archived, cwd
""")
for row in cur.fetchall():
print(row)
# Ubah thread user dari OpenAI menjadi 9Router
cur.execute("""
UPDATE threads
SET model_provider = '9router'
WHERE model_provider = 'openai'
AND (thread_source = 'user' OR thread_source IS NULL)
""")
print("\nOpenAI user/null -> 9router:", cur.rowcount)
# Samakan nama model agar sesuai config 9Router
cur.execute("""
UPDATE threads
SET model = 'conding-combo'
WHERE model_provider = '9router'
AND (thread_source = 'user' OR thread_source IS NULL)
""")
print("Set model conding-combo:", cur.rowcount)
# Buka arsip thread user 9Router agar muncul di history
cur.execute("""
UPDATE threads
SET archived = 0, archived_at = NULL
WHERE model_provider = '9router'
AND (thread_source = 'user' OR thread_source IS NULL)
""")
print("Unarchive user 9router:", cur.rowcount)
conn.commit()
try:
cur.execute("PRAGMA wal_checkpoint(FULL)")
print("WAL checkpoint:", cur.fetchall())
except Exception as e:
print("WAL checkpoint skipped:", e)
print("\nSesudah:")
cur.execute("""
SELECT model_provider, archived, thread_source, cwd, COUNT(*)
FROM threads
GROUP BY model_provider, archived, thread_source, cwd
ORDER BY model_provider, archived, cwd
""")
for row in cur.fetchall():
print(row)
conn.close()
PY
Setelah itu buka kembali Codex App.
Hasil
Setelah patch berhasil, chat lama OpenAI dan chat baru 9Router muncul bersamaan saat config Codex memakai:
model = "conding-combo"
model_provider = "9router"
[model_providers.9router]
name = "9Router"
base_url = "http://127.0.0.1:20128/v1"
wire_api = "responses"
Cara Restore Jika Gagal
Script tadi otomatis membuat backup database di folder seperti ini:
~/.codex/provider-force-merge-YYYYMMDD-HHMMSS/state_5.sqlite.bak
Untuk restore:
cp ~/.codex/provider-force-merge-YYYYMMDD-HHMMSS/state_5.sqlite.bak ~/.codex/state_5.sqlite
Ganti YYYYMMDD-HHMMSS sesuai nama folder backup yang muncul di terminal.
Kesimpulan
Chat Codex Desktop sebenarnya tidak hilang saat berpindah dari OpenAI ke 9Router. History hanya difilter berdasarkan model_provider.
Solusinya adalah menyamakan metadata provider di database lokal Codex:
openai → 9router
Dengan begitu, chat lama OpenAI bisa tampil bersamaan dengan chat baru 9Router.
Tetap lakukan backup sebelum mengedit database, karena cara ini bukan fitur resmi Codex, melainkan workaround lokal.

No comments:
Post a Comment