{"version":3,"file":"vector-handler.cjs","names":[],"sources":["../src/vector-handler.ts"],"sourcesContent":["import type * as http from \"node:http\";\nimport type {\n  VectorCollection,\n  VectorEntry,\n  VectorQuery,\n  QueryResult,\n  QueryHandler,\n} from \"./vector-types.js\";\n\nexport interface VectorState {\n  collections: Map<string, VectorCollection>;\n  queryHandlers: Map<string, QueryHandler>;\n}\n\ninterface RouteResult {\n  handled: boolean;\n}\n\nfunction jsonResponse(res: http.ServerResponse, status: number, body: unknown): void {\n  const payload = JSON.stringify(body);\n  res.writeHead(status, {\n    \"Content-Type\": \"application/json\",\n    \"Content-Length\": String(Buffer.byteLength(payload)),\n  });\n  res.end(payload);\n}\n\nfunction resolveQuery(\n  state: VectorState,\n  collectionName: string,\n  query: VectorQuery,\n): QueryResult[] {\n  const handler = state.queryHandlers.get(collectionName);\n  if (!handler) return [];\n  if (typeof handler === \"function\") return handler(query);\n  return handler;\n}\n\n// ---- Pinecone-compatible endpoints ----\n\nfunction handlePinecone(\n  state: VectorState,\n  req: http.IncomingMessage,\n  res: http.ServerResponse,\n  pathname: string,\n  body: Record<string, unknown>,\n): RouteResult {\n  // POST /query\n  if (req.method === \"POST\" && pathname === \"/query\") {\n    const namespace = (body.namespace as string) ?? \"default\";\n    const collection = state.collections.get(namespace);\n    if (!collection) {\n      jsonResponse(res, 404, { error: { message: `Collection '${namespace}' not found` } });\n      return { handled: true };\n    }\n\n    const query: VectorQuery = {\n      vector: body.vector as number[] | undefined,\n      topK: body.topK as number | undefined,\n      filter: body.filter,\n      collection: namespace,\n    };\n    const results = resolveQuery(state, namespace, query);\n    const topK = query.topK ?? 10;\n    const matches = results.slice(0, topK).map((r) => ({\n      id: r.id,\n      score: r.score,\n      ...(r.metadata !== undefined && { metadata: r.metadata }),\n    }));\n\n    jsonResponse(res, 200, { matches });\n    return { handled: true };\n  }\n\n  // POST /vectors/upsert\n  if (req.method === \"POST\" && pathname === \"/vectors/upsert\") {\n    const vectors = (body.vectors ?? []) as Array<{\n      id: string;\n      values: number[];\n      metadata?: Record<string, unknown>;\n    }>;\n    const namespace = (body.namespace as string) ?? \"default\";\n\n    let collection = state.collections.get(namespace);\n    if (!collection) {\n      const dim = vectors.length > 0 ? vectors[0].values.length : 0;\n      collection = { name: namespace, dimension: dim, vectors: new Map() };\n      state.collections.set(namespace, collection);\n    }\n\n    for (const v of vectors) {\n      const entry: VectorEntry = { id: v.id, values: v.values, metadata: v.metadata };\n      collection.vectors.set(v.id, entry);\n    }\n\n    jsonResponse(res, 200, { upsertedCount: vectors.length });\n    return { handled: true };\n  }\n\n  // POST /vectors/delete\n  if (req.method === \"POST\" && pathname === \"/vectors/delete\") {\n    const ids = (body.ids ?? []) as string[];\n    const namespace = (body.namespace as string) ?? \"default\";\n    const collection = state.collections.get(namespace);\n    if (collection) {\n      for (const id of ids) {\n        collection.vectors.delete(id);\n      }\n    }\n    jsonResponse(res, 200, {});\n    return { handled: true };\n  }\n\n  // GET /describe-index-stats\n  if (req.method === \"GET\" && pathname === \"/describe-index-stats\") {\n    let totalVectorCount = 0;\n    let dimension = 0;\n    for (const col of state.collections.values()) {\n      totalVectorCount += col.vectors.size;\n      if (col.dimension > 0) dimension = col.dimension;\n    }\n    jsonResponse(res, 200, { dimension, totalVectorCount });\n    return { handled: true };\n  }\n\n  return { handled: false };\n}\n\n// ---- Qdrant-compatible endpoints ----\n\nconst QDRANT_SEARCH_RE = /^\\/collections\\/([^/]+)\\/points\\/search$/;\nconst QDRANT_UPSERT_RE = /^\\/collections\\/([^/]+)\\/points$/;\nconst QDRANT_DELETE_RE = /^\\/collections\\/([^/]+)\\/points\\/delete$/;\n\nfunction handleQdrant(\n  state: VectorState,\n  req: http.IncomingMessage,\n  res: http.ServerResponse,\n  pathname: string,\n  body: Record<string, unknown>,\n): RouteResult {\n  // POST /collections/{name}/points/search\n  let match = pathname.match(QDRANT_SEARCH_RE);\n  if (match && req.method === \"POST\") {\n    const name = decodeURIComponent(match[1]);\n    const collection = state.collections.get(name);\n    if (!collection) {\n      jsonResponse(res, 404, { status: { error: `Collection '${name}' not found` } });\n      return { handled: true };\n    }\n\n    const query: VectorQuery = {\n      vector: body.vector as number[] | undefined,\n      topK: body.limit as number | undefined,\n      filter: body.filter,\n      collection: name,\n    };\n    const results = resolveQuery(state, name, query);\n    const limit = (body.limit as number) ?? 10;\n    const result = results.slice(0, limit).map((r) => ({\n      id: r.id,\n      score: r.score,\n      ...(r.metadata !== undefined && { payload: r.metadata }),\n    }));\n\n    jsonResponse(res, 200, { result });\n    return { handled: true };\n  }\n\n  // PUT /collections/{name}/points\n  match = pathname.match(QDRANT_UPSERT_RE);\n  if (match && req.method === \"PUT\") {\n    const name = decodeURIComponent(match[1]);\n    let collection = state.collections.get(name);\n    const points = (body.points ?? []) as Array<{\n      id: string;\n      vector: number[];\n      payload?: Record<string, unknown>;\n    }>;\n\n    if (!collection) {\n      const dim = points.length > 0 ? points[0].vector.length : 0;\n      collection = { name, dimension: dim, vectors: new Map() };\n      state.collections.set(name, collection);\n    }\n\n    for (const p of points) {\n      const entry: VectorEntry = { id: String(p.id), values: p.vector, metadata: p.payload };\n      collection.vectors.set(String(p.id), entry);\n    }\n\n    jsonResponse(res, 200, { status: \"ok\" });\n    return { handled: true };\n  }\n\n  // POST /collections/{name}/points/delete\n  match = pathname.match(QDRANT_DELETE_RE);\n  if (match && req.method === \"POST\") {\n    const name = decodeURIComponent(match[1]);\n    const collection = state.collections.get(name);\n    const points = (body.points ?? []) as string[];\n    if (collection) {\n      for (const id of points) {\n        collection.vectors.delete(String(id));\n      }\n    }\n    jsonResponse(res, 200, { status: \"ok\" });\n    return { handled: true };\n  }\n\n  return { handled: false };\n}\n\n// ---- ChromaDB-compatible endpoints ----\n\nconst CHROMA_QUERY_RE = /^\\/api\\/v1\\/collections\\/([^/]+)\\/query$/;\nconst CHROMA_ADD_RE = /^\\/api\\/v1\\/collections\\/([^/]+)\\/add$/;\nconst CHROMA_COLLECTION_RE = /^\\/api\\/v1\\/collections\\/([^/]+)$/;\nconst CHROMA_COLLECTIONS = \"/api/v1/collections\";\n\nfunction handleChromaDB(\n  state: VectorState,\n  req: http.IncomingMessage,\n  res: http.ServerResponse,\n  pathname: string,\n  body: Record<string, unknown>,\n): RouteResult {\n  // POST /api/v1/collections/{id}/query\n  let match = pathname.match(CHROMA_QUERY_RE);\n  if (match && req.method === \"POST\") {\n    const name = decodeURIComponent(match[1]);\n    const collection = state.collections.get(name);\n    if (!collection) {\n      jsonResponse(res, 404, { error: `Collection '${name}' not found` });\n      return { handled: true };\n    }\n\n    const queryEmbeddings = (body.query_embeddings ?? []) as number[][];\n    const nResults = (body.n_results as number) ?? 10;\n\n    // Process each query embedding\n    const allIds: string[][] = [];\n    const allDistances: number[][] = [];\n    const allMetadatas: Array<Array<Record<string, unknown> | null>> = [];\n\n    for (const embedding of queryEmbeddings) {\n      const query: VectorQuery = {\n        vector: embedding,\n        topK: nResults,\n        filter: body.where,\n        collection: name,\n      };\n      const results = resolveQuery(state, name, query).slice(0, nResults);\n\n      allIds.push(results.map((r) => r.id));\n      allDistances.push(results.map((r) => r.score));\n      allMetadatas.push(results.map((r) => r.metadata ?? null));\n    }\n\n    jsonResponse(res, 200, {\n      ids: allIds,\n      distances: allDistances,\n      metadatas: allMetadatas,\n    });\n    return { handled: true };\n  }\n\n  // POST /api/v1/collections/{id}/add\n  match = pathname.match(CHROMA_ADD_RE);\n  if (match && req.method === \"POST\") {\n    const name = decodeURIComponent(match[1]);\n    let collection = state.collections.get(name);\n\n    const ids = (body.ids ?? []) as string[];\n    const embeddings = (body.embeddings ?? []) as number[][];\n    const metadatas = (body.metadatas ?? []) as Array<Record<string, unknown> | undefined>;\n\n    if (!collection) {\n      const dim = embeddings.length > 0 ? embeddings[0].length : 0;\n      collection = { name, dimension: dim, vectors: new Map() };\n      state.collections.set(name, collection);\n    }\n\n    for (let i = 0; i < ids.length; i++) {\n      const entry: VectorEntry = {\n        id: ids[i],\n        values: embeddings[i] ?? [],\n        metadata: metadatas[i],\n      };\n      collection.vectors.set(ids[i], entry);\n    }\n\n    jsonResponse(res, 200, true);\n    return { handled: true };\n  }\n\n  // GET /api/v1/collections — list collections\n  if (req.method === \"GET\" && pathname === CHROMA_COLLECTIONS) {\n    const collections = Array.from(state.collections.values()).map((c) => ({\n      id: c.name,\n      name: c.name,\n      metadata: null,\n    }));\n    jsonResponse(res, 200, collections);\n    return { handled: true };\n  }\n\n  // DELETE /api/v1/collections/{id}\n  match = pathname.match(CHROMA_COLLECTION_RE);\n  if (match && req.method === \"DELETE\") {\n    const name = decodeURIComponent(match[1]);\n    if (!state.collections.has(name)) {\n      jsonResponse(res, 404, { error: `Collection '${name}' not found` });\n      return { handled: true };\n    }\n    state.collections.delete(name);\n    state.queryHandlers.delete(name);\n    jsonResponse(res, 200, { status: \"ok\" });\n    return { handled: true };\n  }\n\n  return { handled: false };\n}\n\n// ---- Main dispatch ----\n\nexport function createVectorRequestHandler(state: VectorState) {\n  return (\n    req: http.IncomingMessage,\n    res: http.ServerResponse,\n    pathname: string,\n    body: Record<string, unknown>,\n  ): boolean => {\n    const pinecone = handlePinecone(state, req, res, pathname, body);\n    if (pinecone.handled) return true;\n\n    const qdrant = handleQdrant(state, req, res, pathname, body);\n    if (qdrant.handled) return true;\n\n    const chroma = handleChromaDB(state, req, res, pathname, body);\n    if (chroma.handled) return true;\n\n    return false;\n  };\n}\n"],"mappings":";;AAkBA,SAAS,aAAa,KAA0B,QAAgB,MAAqB;CACnF,MAAM,UAAU,KAAK,UAAU,KAAK;AACpC,KAAI,UAAU,QAAQ;EACpB,gBAAgB;EAChB,kBAAkB,OAAO,OAAO,WAAW,QAAQ,CAAC;EACrD,CAAC;AACF,KAAI,IAAI,QAAQ;;AAGlB,SAAS,aACP,OACA,gBACA,OACe;CACf,MAAM,UAAU,MAAM,cAAc,IAAI,eAAe;AACvD,KAAI,CAAC,QAAS,QAAO,EAAE;AACvB,KAAI,OAAO,YAAY,WAAY,QAAO,QAAQ,MAAM;AACxD,QAAO;;AAKT,SAAS,eACP,OACA,KACA,KACA,UACA,MACa;AAEb,KAAI,IAAI,WAAW,UAAU,aAAa,UAAU;EAClD,MAAM,YAAa,KAAK,aAAwB;AAEhD,MAAI,CADe,MAAM,YAAY,IAAI,UAAU,EAClC;AACf,gBAAa,KAAK,KAAK,EAAE,OAAO,EAAE,SAAS,eAAe,UAAU,cAAc,EAAE,CAAC;AACrF,UAAO,EAAE,SAAS,MAAM;;EAG1B,MAAM,QAAqB;GACzB,QAAQ,KAAK;GACb,MAAM,KAAK;GACX,QAAQ,KAAK;GACb,YAAY;GACb;EACD,MAAM,UAAU,aAAa,OAAO,WAAW,MAAM;EACrD,MAAM,OAAO,MAAM,QAAQ;AAO3B,eAAa,KAAK,KAAK,EAAE,SANT,QAAQ,MAAM,GAAG,KAAK,CAAC,KAAK,OAAO;GACjD,IAAI,EAAE;GACN,OAAO,EAAE;GACT,GAAI,EAAE,aAAa,UAAa,EAAE,UAAU,EAAE,UAAU;GACzD,EAAE,EAE+B,CAAC;AACnC,SAAO,EAAE,SAAS,MAAM;;AAI1B,KAAI,IAAI,WAAW,UAAU,aAAa,mBAAmB;EAC3D,MAAM,UAAW,KAAK,WAAW,EAAE;EAKnC,MAAM,YAAa,KAAK,aAAwB;EAEhD,IAAI,aAAa,MAAM,YAAY,IAAI,UAAU;AACjD,MAAI,CAAC,YAAY;AAEf,gBAAa;IAAE,MAAM;IAAW,WADpB,QAAQ,SAAS,IAAI,QAAQ,GAAG,OAAO,SAAS;IACZ,yBAAS,IAAI,KAAK;IAAE;AACpE,SAAM,YAAY,IAAI,WAAW,WAAW;;AAG9C,OAAK,MAAM,KAAK,SAAS;GACvB,MAAM,QAAqB;IAAE,IAAI,EAAE;IAAI,QAAQ,EAAE;IAAQ,UAAU,EAAE;IAAU;AAC/E,cAAW,QAAQ,IAAI,EAAE,IAAI,MAAM;;AAGrC,eAAa,KAAK,KAAK,EAAE,eAAe,QAAQ,QAAQ,CAAC;AACzD,SAAO,EAAE,SAAS,MAAM;;AAI1B,KAAI,IAAI,WAAW,UAAU,aAAa,mBAAmB;EAC3D,MAAM,MAAO,KAAK,OAAO,EAAE;EAC3B,MAAM,YAAa,KAAK,aAAwB;EAChD,MAAM,aAAa,MAAM,YAAY,IAAI,UAAU;AACnD,MAAI,WACF,MAAK,MAAM,MAAM,IACf,YAAW,QAAQ,OAAO,GAAG;AAGjC,eAAa,KAAK,KAAK,EAAE,CAAC;AAC1B,SAAO,EAAE,SAAS,MAAM;;AAI1B,KAAI,IAAI,WAAW,SAAS,aAAa,yBAAyB;EAChE,IAAI,mBAAmB;EACvB,IAAI,YAAY;AAChB,OAAK,MAAM,OAAO,MAAM,YAAY,QAAQ,EAAE;AAC5C,uBAAoB,IAAI,QAAQ;AAChC,OAAI,IAAI,YAAY,EAAG,aAAY,IAAI;;AAEzC,eAAa,KAAK,KAAK;GAAE;GAAW;GAAkB,CAAC;AACvD,SAAO,EAAE,SAAS,MAAM;;AAG1B,QAAO,EAAE,SAAS,OAAO;;AAK3B,MAAM,mBAAmB;AACzB,MAAM,mBAAmB;AACzB,MAAM,mBAAmB;AAEzB,SAAS,aACP,OACA,KACA,KACA,UACA,MACa;CAEb,IAAI,QAAQ,SAAS,MAAM,iBAAiB;AAC5C,KAAI,SAAS,IAAI,WAAW,QAAQ;EAClC,MAAM,OAAO,mBAAmB,MAAM,GAAG;AAEzC,MAAI,CADe,MAAM,YAAY,IAAI,KAAK,EAC7B;AACf,gBAAa,KAAK,KAAK,EAAE,QAAQ,EAAE,OAAO,eAAe,KAAK,cAAc,EAAE,CAAC;AAC/E,UAAO,EAAE,SAAS,MAAM;;EAS1B,MAAM,UAAU,aAAa,OAAO,MANT;GACzB,QAAQ,KAAK;GACb,MAAM,KAAK;GACX,QAAQ,KAAK;GACb,YAAY;GACb,CAC+C;EAChD,MAAM,QAAS,KAAK,SAAoB;AAOxC,eAAa,KAAK,KAAK,EAAE,QANV,QAAQ,MAAM,GAAG,MAAM,CAAC,KAAK,OAAO;GACjD,IAAI,EAAE;GACN,OAAO,EAAE;GACT,GAAI,EAAE,aAAa,UAAa,EAAE,SAAS,EAAE,UAAU;GACxD,EAAE,EAE8B,CAAC;AAClC,SAAO,EAAE,SAAS,MAAM;;AAI1B,SAAQ,SAAS,MAAM,iBAAiB;AACxC,KAAI,SAAS,IAAI,WAAW,OAAO;EACjC,MAAM,OAAO,mBAAmB,MAAM,GAAG;EACzC,IAAI,aAAa,MAAM,YAAY,IAAI,KAAK;EAC5C,MAAM,SAAU,KAAK,UAAU,EAAE;AAMjC,MAAI,CAAC,YAAY;AAEf,gBAAa;IAAE;IAAM,WADT,OAAO,SAAS,IAAI,OAAO,GAAG,OAAO,SAAS;IACrB,yBAAS,IAAI,KAAK;IAAE;AACzD,SAAM,YAAY,IAAI,MAAM,WAAW;;AAGzC,OAAK,MAAM,KAAK,QAAQ;GACtB,MAAM,QAAqB;IAAE,IAAI,OAAO,EAAE,GAAG;IAAE,QAAQ,EAAE;IAAQ,UAAU,EAAE;IAAS;AACtF,cAAW,QAAQ,IAAI,OAAO,EAAE,GAAG,EAAE,MAAM;;AAG7C,eAAa,KAAK,KAAK,EAAE,QAAQ,MAAM,CAAC;AACxC,SAAO,EAAE,SAAS,MAAM;;AAI1B,SAAQ,SAAS,MAAM,iBAAiB;AACxC,KAAI,SAAS,IAAI,WAAW,QAAQ;EAClC,MAAM,OAAO,mBAAmB,MAAM,GAAG;EACzC,MAAM,aAAa,MAAM,YAAY,IAAI,KAAK;EAC9C,MAAM,SAAU,KAAK,UAAU,EAAE;AACjC,MAAI,WACF,MAAK,MAAM,MAAM,OACf,YAAW,QAAQ,OAAO,OAAO,GAAG,CAAC;AAGzC,eAAa,KAAK,KAAK,EAAE,QAAQ,MAAM,CAAC;AACxC,SAAO,EAAE,SAAS,MAAM;;AAG1B,QAAO,EAAE,SAAS,OAAO;;AAK3B,MAAM,kBAAkB;AACxB,MAAM,gBAAgB;AACtB,MAAM,uBAAuB;AAC7B,MAAM,qBAAqB;AAE3B,SAAS,eACP,OACA,KACA,KACA,UACA,MACa;CAEb,IAAI,QAAQ,SAAS,MAAM,gBAAgB;AAC3C,KAAI,SAAS,IAAI,WAAW,QAAQ;EAClC,MAAM,OAAO,mBAAmB,MAAM,GAAG;AAEzC,MAAI,CADe,MAAM,YAAY,IAAI,KAAK,EAC7B;AACf,gBAAa,KAAK,KAAK,EAAE,OAAO,eAAe,KAAK,cAAc,CAAC;AACnE,UAAO,EAAE,SAAS,MAAM;;EAG1B,MAAM,kBAAmB,KAAK,oBAAoB,EAAE;EACpD,MAAM,WAAY,KAAK,aAAwB;EAG/C,MAAM,SAAqB,EAAE;EAC7B,MAAM,eAA2B,EAAE;EACnC,MAAM,eAA6D,EAAE;AAErE,OAAK,MAAM,aAAa,iBAAiB;GAOvC,MAAM,UAAU,aAAa,OAAO,MANT;IACzB,QAAQ;IACR,MAAM;IACN,QAAQ,KAAK;IACb,YAAY;IACb,CAC+C,CAAC,MAAM,GAAG,SAAS;AAEnE,UAAO,KAAK,QAAQ,KAAK,MAAM,EAAE,GAAG,CAAC;AACrC,gBAAa,KAAK,QAAQ,KAAK,MAAM,EAAE,MAAM,CAAC;AAC9C,gBAAa,KAAK,QAAQ,KAAK,MAAM,EAAE,YAAY,KAAK,CAAC;;AAG3D,eAAa,KAAK,KAAK;GACrB,KAAK;GACL,WAAW;GACX,WAAW;GACZ,CAAC;AACF,SAAO,EAAE,SAAS,MAAM;;AAI1B,SAAQ,SAAS,MAAM,cAAc;AACrC,KAAI,SAAS,IAAI,WAAW,QAAQ;EAClC,MAAM,OAAO,mBAAmB,MAAM,GAAG;EACzC,IAAI,aAAa,MAAM,YAAY,IAAI,KAAK;EAE5C,MAAM,MAAO,KAAK,OAAO,EAAE;EAC3B,MAAM,aAAc,KAAK,cAAc,EAAE;EACzC,MAAM,YAAa,KAAK,aAAa,EAAE;AAEvC,MAAI,CAAC,YAAY;AAEf,gBAAa;IAAE;IAAM,WADT,WAAW,SAAS,IAAI,WAAW,GAAG,SAAS;IACtB,yBAAS,IAAI,KAAK;IAAE;AACzD,SAAM,YAAY,IAAI,MAAM,WAAW;;AAGzC,OAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;GACnC,MAAM,QAAqB;IACzB,IAAI,IAAI;IACR,QAAQ,WAAW,MAAM,EAAE;IAC3B,UAAU,UAAU;IACrB;AACD,cAAW,QAAQ,IAAI,IAAI,IAAI,MAAM;;AAGvC,eAAa,KAAK,KAAK,KAAK;AAC5B,SAAO,EAAE,SAAS,MAAM;;AAI1B,KAAI,IAAI,WAAW,SAAS,aAAa,oBAAoB;AAM3D,eAAa,KAAK,KALE,MAAM,KAAK,MAAM,YAAY,QAAQ,CAAC,CAAC,KAAK,OAAO;GACrE,IAAI,EAAE;GACN,MAAM,EAAE;GACR,UAAU;GACX,EAAE,CACgC;AACnC,SAAO,EAAE,SAAS,MAAM;;AAI1B,SAAQ,SAAS,MAAM,qBAAqB;AAC5C,KAAI,SAAS,IAAI,WAAW,UAAU;EACpC,MAAM,OAAO,mBAAmB,MAAM,GAAG;AACzC,MAAI,CAAC,MAAM,YAAY,IAAI,KAAK,EAAE;AAChC,gBAAa,KAAK,KAAK,EAAE,OAAO,eAAe,KAAK,cAAc,CAAC;AACnE,UAAO,EAAE,SAAS,MAAM;;AAE1B,QAAM,YAAY,OAAO,KAAK;AAC9B,QAAM,cAAc,OAAO,KAAK;AAChC,eAAa,KAAK,KAAK,EAAE,QAAQ,MAAM,CAAC;AACxC,SAAO,EAAE,SAAS,MAAM;;AAG1B,QAAO,EAAE,SAAS,OAAO;;AAK3B,SAAgB,2BAA2B,OAAoB;AAC7D,SACE,KACA,KACA,UACA,SACY;AAEZ,MADiB,eAAe,OAAO,KAAK,KAAK,UAAU,KAAK,CACnD,QAAS,QAAO;AAG7B,MADe,aAAa,OAAO,KAAK,KAAK,UAAU,KAAK,CACjD,QAAS,QAAO;AAG3B,MADe,eAAe,OAAO,KAAK,KAAK,UAAU,KAAK,CACnD,QAAS,QAAO;AAE3B,SAAO"}