WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Copy existing vectors to an isolated BM25 collection, without re-embedding.
|
||||
|
||||
The manifest is an export of the acceptance DB's chunk IDs. Source collections
|
||||
are read-only; acknowledgements and a manifest digest make retries resumable.
|
||||
"""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
FIELDS = ['id', 'embedding', 'content', 'source_id', 'source_type', 'chunk_id',
|
||||
'knowledge_id', 'knowledge_base_id', 'tag_id', 'is_enabled']
|
||||
|
||||
|
||||
def post(base, path, payload):
|
||||
request = urllib.request.Request(base + '/v2/vectordb/' + path,
|
||||
data=json.dumps(payload).encode(),
|
||||
headers={'Content-Type': 'application/json'})
|
||||
with urllib.request.urlopen(request, timeout=120) as response:
|
||||
result = json.load(response)
|
||||
if result.get('code') != 0:
|
||||
raise RuntimeError(f"{path}: {result.get('message', result.get('code'))}")
|
||||
return result.get('data')
|
||||
|
||||
|
||||
def save(path, data):
|
||||
temporary = path.with_suffix('.tmp')
|
||||
temporary.write_text(json.dumps(data, ensure_ascii=False, indent=2) + '\n')
|
||||
os.replace(temporary, path)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--manifest', type=Path, required=True)
|
||||
parser.add_argument('--state', type=Path, required=True)
|
||||
parser.add_argument('--target', default='yawan_acceptance_hybrid_512')
|
||||
parser.add_argument('--source', default='weknora_embeddings_512')
|
||||
args = parser.parse_args()
|
||||
if not args.target.startswith('yawan_acceptance_') or args.target == args.source:
|
||||
parser.error('target must be a distinct acceptance collection')
|
||||
base = 'http://127.0.0.1:19530'
|
||||
raw = args.manifest.read_bytes()
|
||||
manifest = json.loads(raw)
|
||||
digest = hashlib.sha256(raw).hexdigest()
|
||||
state = json.loads(args.state.read_text()) if args.state.exists() else {
|
||||
'manifest_sha256': digest, 'source': args.source, 'target': args.target,
|
||||
'offset': 0, 'copied': 0, 'missing': [], 'started_at': time.time(),
|
||||
}
|
||||
for key, value in [('manifest_sha256', digest), ('source', args.source), ('target', args.target)]:
|
||||
if state[key] != value:
|
||||
raise RuntimeError(f'{key} differs from checkpoint')
|
||||
description = post(base, 'collections/describe', {'collectionName': args.target})
|
||||
if 'content_sparse' not in {f['name'] for f in description['fields']}:
|
||||
raise RuntimeError('target must have BM25 schema before copying')
|
||||
for offset in range(state['offset'], len(manifest), 256):
|
||||
batch = manifest[offset:offset + 256]
|
||||
expected = {x['id']: x for x in batch}
|
||||
records = post(base, 'entities/query', {
|
||||
'collectionName': args.source,
|
||||
'filter': 'chunk_id in ' + json.dumps(list(expected)),
|
||||
'outputFields': FIELDS, 'limit': len(batch) * 2,
|
||||
'consistencyLevel': 'Strong',
|
||||
})
|
||||
found = set()
|
||||
for row in records:
|
||||
entry = expected.get(row['chunk_id'])
|
||||
if not entry or row['knowledge_base_id'] != entry['knowledge_base_id'] or row['knowledge_id'] != entry['knowledge_id']:
|
||||
raise RuntimeError('source scope differs from acceptance manifest')
|
||||
if row['chunk_id'] in found:
|
||||
raise RuntimeError('duplicate source vectors for chunk')
|
||||
found.add(row['chunk_id'])
|
||||
if records:
|
||||
result = post(base, 'entities/upsert', {'collectionName': args.target, 'data': records})
|
||||
if result.get('upsertCount') != len(records):
|
||||
raise RuntimeError('target did not acknowledge every vector')
|
||||
state['missing'].extend(sorted(expected.keys() - found))
|
||||
state['copied'] += len(records)
|
||||
state['offset'] = offset + len(batch)
|
||||
state['updated_at'] = time.time()
|
||||
save(args.state, state)
|
||||
if offset % (256 * 20) == 0 or state['offset'] == len(manifest):
|
||||
print(json.dumps({'processed': state['offset'], 'total': len(manifest),
|
||||
'copied': state['copied'], 'missing': len(state['missing'])}), flush=True)
|
||||
state['complete'] = not state['missing']
|
||||
save(args.state, state)
|
||||
print(json.dumps({'complete': state['complete'], 'copied': state['copied'],
|
||||
'missing': len(state['missing'])}), flush=True)
|
||||
return 0 if state['complete'] else 1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user