主题
在目录里新增、更新或删除文件之后,需要同步索引才能反映这些变更。同步会重新解析有变化的文件,并把更新后的块重新导出到向量库。
创建索引时会自动触发首次同步。此后的更新请用同步端点。
触发同步
- Python
- TypeScript
- Go
- Java
- CLI
await client.indexes.sync("<your-index-id>")await client.indexes.sync("<your-index-id>");_, err := client.Indexes.Sync(ctx, "<your-index-id>", llamacloud.IndexSyncParams{})
if err != nil {
log.Fatal(err)
}client.indexes().sync("<your-index-id>");终端窗口
llp beta:indexes sync --index-id "<your-index-id>"同步是异步运行的。轮询索引以得知它何时完成 —— 一个避免竞态的轮询循环见下文的典型流程。
注意
同一个索引一次只能运行一个同步。若已有同步在进行中,API 会返回 409 Conflict。同步端点还另有速率限制 —— 短时间内反复调用会返回 429 Too Many Requests。请触发一次同步后改轮询索引状态,不要在一个循环里反复调同步端点。
下面的流程假设你的同步调用已被接受,因此结束的那次运行确实覆盖了你的变更。若你收到 409 或 429,那个已在运行的同步可能开始于你加入文件之前 —— 它结束时你的文件仍可能不在索引里。索引一个文件的 cookbook 演示了如何通过检查该文件本身来应对这种情况。
典型流程
- 在目录里上传新文件或移除过期文件。
- 读取索引当前的
last_synced_at与status,然后调用同步端点一次。 - 轮询索引,直到
status为ready并且last_synced_at已推进过第 2 步读到的值;或者status在第 2 步之后发生变化并变为failed。此后即可恢复检索或对话。
注意
触发同步后不要只靠轮询 status == "ready" 来判断。在新同步真正开始前,索引会一直保持它此前的 ready 状态,因此单看状态可能立刻返回、在还没做任何工作时就报告成功。把 last_synced_at 与你在触发之前读到的值做比较,才能确认新的运行真的结束了。在一次失败的构建之后重新同步时,failed 状态也会同样滞留;而失败的运行不会推进 last_synced_at,所以只有当状态自你触发以来确实变化过,才能把 failed 当作本次同步的结果。请给循环设置超时并在两次轮询之间退避,以免它无限空转。
只有 ready 与 failed 这两个 status 取值在各版本间保持稳定 —— 其他取值(pending、syncing、exporting 等)一律当作「还没完成」即可。
下载一份只有一页的指标报告作为 new-report.pdf,加入目录:
终端窗口
curl -fL -o new-report.pdf https://raw.githubusercontent.com/run-llama/llama-cloud-py/main/examples/extract/test-files/slide/saas_slide.pdf- Python
- TypeScript
- Go
- Java
- CLI
import asyncio
import time
# Add a new file to the directory
with open("new-report.pdf", "rb") as f:
file_obj = await client.files.create(file=f, purpose="user_data")
await client.beta.directories.files.add(
directory_id,
file_id=file_obj.id,
)
# Note the state of the index before the sync. A freshly triggered sync keeps
# reporting the previous status until it starts, so we wait for "ready" with
# a newer last_synced_at, and trust "failed" only once the status has changed.
prev = await client.indexes.get(index_id)
before = prev.last_synced_at
before_status = prev.metadata["status"] if prev.metadata else "unknown"
# Trigger a sync (call this once — the endpoint is rate limited)
await client.indexes.sync(index_id)
# Poll until this sync finishes, backing off between attempts and
# giving up after a deadline.
deadline = time.monotonic() + 600 # 10 minutes
delay = 2
started = False # True once the status differs from before the trigger
while True:
idx = await client.indexes.get(index_id)
status = idx.metadata["status"] if idx.metadata else "unknown"
started = started or status != before_status
if status == "ready" and idx.last_synced_at != before:
print("Sync complete!")
break
if status == "failed" and started:
print("Sync failed:", idx.metadata["error_message"])
break
if time.monotonic() >= deadline:
raise TimeoutError("Sync did not complete within 10 minutes")
await asyncio.sleep(delay)
delay = min(delay * 2, 30) # exponential backoff, capped at 30simport fs from "fs";
// Add a new file
const fileObj = await client.files.create({
file: fs.createReadStream("new-report.pdf"),
purpose: "user_data",
});
await client.beta.directories.files.add(directoryId, {
file_id: fileObj.id,
});
// Note the state of the index before the sync. A freshly triggered sync keeps
// reporting the previous status until it starts, so we wait for "ready" with
// a newer last_synced_at, and trust "failed" only once the status has changed.
const prev = await client.indexes.get(indexId);
const before = prev.last_synced_at;
const beforeStatus = (prev.metadata?.status as string) ?? "unknown";
// Trigger a sync (call this once — the endpoint is rate limited)
await client.indexes.sync(indexId);
// Poll until this sync finishes, backing off between attempts and
// giving up after a deadline.
const deadline = Date.now() + 600_000; // 10 minutes
let delay = 2000;
let started = false; // true once the status differs from before the trigger
while (true) {
const idx = await client.indexes.get(indexId);
const status = (idx.metadata?.status as string) ?? "unknown";
started = started || status !== beforeStatus;
if (status === "ready" && idx.last_synced_at !== before) {
console.log("Sync complete!");
break;
} else if (status === "failed" && started) {
console.error("Sync failed:", idx.metadata?.error_message);
break;
} else if (Date.now() >= deadline) {
throw new Error("Sync did not complete within 10 minutes");
}
await new Promise((r) => setTimeout(r, delay));
delay = Math.min(delay * 2, 30_000); // exponential backoff, capped at 30s
}// Add a new file to the directory
f, err := os.Open("new-report.pdf")
if err != nil {
log.Fatal(err)
}
defer f.Close()
fileObj, err := client.Files.New(ctx, llamacloud.FileNewParams{
File: f,
Purpose: "user_data",
})
if err != nil {
log.Fatal(err)
}
_, err = client.Beta.Directories.Files.Add(ctx, directoryID, llamacloud.BetaDirectoryFileAddParams{
FileID: fileObj.ID,
})
if err != nil {
log.Fatal(err)
}
// Note the state of the index before the sync. A freshly triggered sync keeps
// reporting the previous status until it starts, so we wait for "ready" with
// a newer LastSyncedAt, and trust "failed" only once the status has changed.
idxBefore, err := client.Indexes.Get(ctx, indexID, llamacloud.IndexGetParams{})
if err != nil {
log.Fatal(err)
}
before := idxBefore.LastSyncedAt
beforeStatus, ok := idxBefore.Metadata["status"].(string)
if !ok {
beforeStatus = "unknown"
}
// Trigger a sync (call this once — the endpoint is rate limited)
_, err = client.Indexes.Sync(ctx, indexID, llamacloud.IndexSyncParams{})
if err != nil {
log.Fatal(err)
}
// Poll until this sync finishes, backing off between attempts and
// giving up after a deadline.
deadline := time.Now().Add(10 * time.Minute)
delay := 2 * time.Second
started := false // true once the status differs from before the trigger
for {
idx, err := client.Indexes.Get(ctx, indexID, llamacloud.IndexGetParams{})
if err != nil {
log.Fatal(err)
}
status, ok := idx.Metadata["status"].(string)
if !ok {
status = "unknown"
}
if status != beforeStatus {
started = true
}
if status == "ready" && !idx.LastSyncedAt.Equal(before) {
fmt.Println("Sync complete!")
break
} else if status == "failed" && started {
fmt.Println("Sync failed:", idx.Metadata["error_message"])
break
} else if time.Now().After(deadline) {
log.Fatal("Sync did not complete within 10 minutes")
}
time.Sleep(delay)
if delay < 30*time.Second {
delay *= 2
if delay > 30*time.Second {
delay = 30 * time.Second
}
}
}import java.nio.file.Paths;
import java.time.Duration;
import java.time.Instant;
import java.time.OffsetDateTime;
import java.util.Optional;
import ai.llamaindex.llamacloud.models.beta.directories.files.FileAddParams;
import ai.llamaindex.llamacloud.models.indexes.IndexGetResponse;
import ai.llamaindex.llamacloud.models.files.FileCreateParams;
import ai.llamaindex.llamacloud.models.files.FileCreateResponse;
// Add a new file to the directory
FileCreateResponse fileObj = client.files().create(
FileCreateParams.builder()
.file(Paths.get("new-report.pdf"))
.purpose("user_data")
.build());
client.beta().directories().files().add(
directoryId,
FileAddParams.builder()
.fileId(fileObj.id())
.build());
// Note the state of the index before the sync. A freshly triggered sync keeps
// reporting the previous status until it starts, so we wait for "ready" with
// a newer lastSyncedAt, and trust "failed" only once the status has changed.
IndexGetResponse prev = client.indexes().get(indexId);
Optional<OffsetDateTime> before = prev.lastSyncedAt();
String beforeStatus = prev.metadata()
.map(m -> m._additionalProperties().get("status"))
.map(v -> v.convert(String.class))
.orElse("unknown");
// Trigger a sync (call this once — the endpoint is rate limited)
client.indexes().sync(indexId);
// Poll until this sync finishes, backing off between attempts and
// giving up after a deadline.
Instant deadline = Instant.now().plus(Duration.ofMinutes(10));
long delayMs = 2000;
boolean started = false; // true once the status differs from before the trigger
while (true) {
IndexGetResponse idx = client.indexes().get(indexId);
String status = idx.metadata()
.map(m -> m._additionalProperties().get("status"))
.map(v -> v.convert(String.class))
.orElse("unknown");
started = started || !status.equals(beforeStatus);
if (status.equals("ready") && !idx.lastSyncedAt().equals(before)) {
System.out.println("Sync complete!");
break;
} else if (status.equals("failed") && started) {
Optional<String> errOpt = idx.metadata()
.map(m -> m._additionalProperties().get("error_message"))
.map(v -> v.convert(String.class));
System.out.println("Sync failed: " + errOpt.orElse(""));
break;
} else if (Instant.now().isAfter(deadline)) {
throw new RuntimeException("Sync did not complete within 10 minutes");
}
Thread.sleep(delayMs);
delayMs = Math.min(delayMs * 2, 30000); // exponential backoff, capped at 30s
}终端窗口
# Add a new file to the directory
FILE_ID=$(llp files create \
--file ./new-report.pdf \
--purpose user_data | jq -r '.id')
llp beta:directories:files add \
--directory-id "$DIRECTORY_ID" \
--file-id "$FILE_ID"
# Note the state of the index before the sync. A freshly triggered sync keeps
# reporting the previous status until it starts, so we wait for "ready" with
# a newer last_synced_at, and trust "failed" only once the status has changed.
PREV=$(llp beta:indexes get --index-id "$INDEX_ID")
BEFORE=$(printf '%s\n' "$PREV" | jq -r '.last_synced_at // ""')
BEFORE_STATUS=$(printf '%s\n' "$PREV" | jq -r '.metadata.status // "unknown"')
# Trigger a sync (call this once — the endpoint is rate limited)
llp beta:indexes sync --index-id "$INDEX_ID"
# Poll until this sync finishes, backing off between attempts and
# giving up after a deadline.
DEADLINE=$(( $(date +%s) + 600 )) # 10 minutes
DELAY=2
STARTED=
while true; do
IDX=$(llp beta:indexes get --index-id "$INDEX_ID") || { printf '%s\n' "$IDX" >&2; break; }
STATUS=$(printf '%s\n' "$IDX" | jq -r '.metadata.status // "unknown"')
SYNCED_AT=$(printf '%s\n' "$IDX" | jq -r '.last_synced_at // ""')
if [ "$STATUS" != "$BEFORE_STATUS" ]; then STARTED=1; fi
if [ "$STATUS" = "ready" ] && [ "$SYNCED_AT" != "$BEFORE" ]; then
echo 'Sync complete!'
break
elif [ "$STATUS" = "failed" ] && [ -n "$STARTED" ]; then
printf '%s\n' "$IDX" | jq -r '"Sync failed: \(.metadata.error_message // .)"' >&2
break
elif [ "$(date +%s)" -ge "$DEADLINE" ]; then
echo "Sync did not complete within 10 minutes" >&2
break
fi
sleep "$DELAY"
DELAY=$(( DELAY * 2 ))
[ "$DELAY" -gt 30 ] && DELAY=30
done管理索引
获取索引
- Python
- TypeScript
- Go
- Java
- CLI
# Get an index by ID
idx = await client.indexes.get("<your-index-id>")
print(f"{idx.id}: status={idx.metadata['status']}")// Get an index by ID
const idx = await client.indexes.get("<your-index-id>");
console.log(`${idx.id}: status=${idx.metadata?.status}`);// Get an index by ID
idx, err := client.Indexes.Get(ctx, "<your-index-id>", llamacloud.IndexGetParams{})
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s: status=%v\n", idx.ID, idx.Metadata["status"])import java.util.Optional;
import ai.llamaindex.llamacloud.models.indexes.IndexGetResponse;
// Get an index by ID
IndexGetResponse idx = client.indexes().get("<your-index-id>");
Optional<String> statusOpt = idx.metadata()
.map(m -> m._additionalProperties().get("status"))
.map(v -> v.convert(String.class));
String status = statusOpt.orElse("unknown");
System.out.println(idx.id() + ": status=" + status);终端窗口
# Get an index by ID
llp beta:indexes get --index-id "<your-index-id>" \
| jq -r '"\(.id): status=\(.metadata.status)"'删除索引
删除索引会移除其同步与导出配置。源目录及其文件不受影响。
- Python
- TypeScript
- Go
- Java
- CLI
await client.indexes.delete("<your-index-id>")await client.indexes.delete("<your-index-id>");err := client.Indexes.Delete(ctx, "<your-index-id>", llamacloud.IndexDeleteParams{})
if err != nil {
log.Fatal(err)
}client.indexes().delete("<your-index-id>");终端窗口
llp beta:indexes delete --index-id "<your-index-id>"