1+ import { ChromaClient } from 'chromadb' ;
2+ import { spawn } from 'child_process' ;
3+ import aiConfig from '../../config.mjs' ;
4+
5+ // This will hold the child process object for the ChromaDB server
6+ let chromaProcess = null ;
7+
8+ /**
9+ * Checks if a ChromaDB instance is already running on the configured port.
10+ * @returns {Promise<boolean> }
11+ */
12+ async function isDbRunning ( ) {
13+ try {
14+ const { host, port } = aiConfig . memory ;
15+ const client = new ChromaClient ( { host, port } ) ;
16+ await client . heartbeat ( ) ;
17+ return true ;
18+ } catch ( e ) {
19+ return false ;
20+ }
21+ }
22+
23+ /**
24+ * Starts the ChromaDB server as a background process, if not already running.
25+ * @returns {Promise<object> } A promise that resolves with the status.
26+ */
27+ async function start_database ( ) {
28+ if ( chromaProcess && ! chromaProcess . killed ) {
29+ return { status : 'already_running' , pid : chromaProcess . pid , detail : 'Server was started by this process.' } ;
30+ }
31+
32+ if ( await isDbRunning ( ) ) {
33+ return { status : 'already_running' , pid : null , detail : 'Server was started externally.' } ;
34+ }
35+
36+ return new Promise ( ( resolve , reject ) => {
37+ const { port, path : dbPath } = aiConfig . memory ;
38+ const args = [ 'run' , '--path' , dbPath , '--port' , port . toString ( ) ] ;
39+
40+ chromaProcess = spawn ( 'chroma' , args , {
41+ detached : true ,
42+ stdio : 'ignore'
43+ } ) ;
44+
45+ chromaProcess . on ( 'spawn' , ( ) => {
46+ console . log ( `ChromaDB (Memory Core) process started with PID: ${ chromaProcess . pid } ` ) ;
47+ resolve ( { status : 'started' , pid : chromaProcess . pid } ) ;
48+ } ) ;
49+
50+ chromaProcess . on ( 'error' , ( err ) => {
51+ console . error ( 'Failed to start ChromaDB (Memory Core) process:' , err ) ;
52+ chromaProcess = null ;
53+ reject ( err ) ;
54+ } ) ;
55+
56+ chromaProcess . unref ( ) ;
57+ } ) ;
58+ }
59+
60+ /**
61+ * Stops the ChromaDB server process if it was started by this server.
62+ * @returns {Promise<object> } A promise that resolves with the status.
63+ */
64+ async function stop_database ( ) {
65+ if ( ! chromaProcess || chromaProcess . killed ) {
66+ return { status : 'not_running' , detail : 'No process was started by this server.' } ;
67+ }
68+
69+ return new Promise ( ( resolve ) => {
70+ chromaProcess . on ( 'exit' , ( ) => {
71+ console . log ( `ChromaDB process with PID: ${ chromaProcess . pid } has been stopped.` ) ;
72+ chromaProcess = null ;
73+ resolve ( { status : 'stopped' } ) ;
74+ } ) ;
75+
76+ process . kill ( - chromaProcess . pid , 'SIGTERM' ) ;
77+ } ) ;
78+ }
79+
80+ /**
81+ * Gets the status of the ChromaDB process.
82+ * @returns {object } The status of the ChromaDB process.
83+ */
84+ function get_database_status ( ) {
85+ if ( chromaProcess && ! chromaProcess . killed ) {
86+ return { running : true , pid : chromaProcess . pid , managed : true } ;
87+ }
88+ return { running : false , pid : null , managed : false } ;
89+ }
90+
91+ export {
92+ start_database ,
93+ stop_database ,
94+ get_database_status
95+ } ;
0 commit comments