mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-03-13 07:18:19 +00:00
Compare commits
14 Commits
1.10.2a1
...
gl/fix/add
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d36f53312c | ||
|
|
e303ca4243 | ||
|
|
5a4f6956b3 | ||
|
|
3949d9f4d0 | ||
|
|
48eb7c6937 | ||
|
|
4d82b08fb2 | ||
|
|
fbd9b800d3 | ||
|
|
10099757dd | ||
|
|
a6e4d35bb9 | ||
|
|
a41cfbd9f6 | ||
|
|
0228445080 | ||
|
|
d2a156f244 | ||
|
|
d8e38f2f0b | ||
|
|
542afe61a8 |
@@ -4,6 +4,39 @@ description: "Product updates, improvements, and bug fixes for CrewAI"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="Mar 11, 2026">
|
||||
## v1.10.2a1
|
||||
|
||||
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.10.2a1)
|
||||
|
||||
## What's Changed
|
||||
|
||||
### Features
|
||||
- Add support for tool search, saving tokens, and dynamically injecting appropriate tools during execution for Anthropics.
|
||||
- Introduce more Brave Search tools.
|
||||
- Create action for nightly releases.
|
||||
|
||||
### Bug Fixes
|
||||
- Fix LockException under concurrent multi-process execution.
|
||||
- Resolve issues with grouping parallel tool results in a single user message.
|
||||
- Address MCP tools resolutions and eliminate all shared mutable connections.
|
||||
- Update LLM parameter handling in the human_feedback function.
|
||||
- Add missing list/dict methods to LockedListProxy and LockedDictProxy.
|
||||
- Propagate contextvars context to parallel tool call threads.
|
||||
- Bump gitpython dependency to >=3.1.41 to resolve CVE path traversal vulnerability.
|
||||
|
||||
### Refactoring
|
||||
- Refactor memory classes to be serializable.
|
||||
|
||||
### Documentation
|
||||
- Update changelog and version for v1.10.1.
|
||||
|
||||
## Contributors
|
||||
|
||||
@akaKuruma, @github-actions[bot], @giulio-leone, @greysonlalonde, @joaomdmoura, @jonathansampson, @lorenzejay, @lucasgomide, @mattatcha
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="Mar 04, 2026">
|
||||
## v1.10.1
|
||||
|
||||
|
||||
@@ -4,6 +4,39 @@ description: "CrewAI의 제품 업데이트, 개선 사항 및 버그 수정"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="2026년 3월 11일">
|
||||
## v1.10.2a1
|
||||
|
||||
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.10.2a1)
|
||||
|
||||
## 변경 사항
|
||||
|
||||
### 기능
|
||||
- Anthropics에 대한 도구 검색 지원 추가, 토큰 저장, 실행 중 적절한 도구를 동적으로 주입하는 기능 추가.
|
||||
- 더 많은 Brave Search 도구 도입.
|
||||
- 야간 릴리스를 위한 액션 생성.
|
||||
|
||||
### 버그 수정
|
||||
- 동시 다중 프로세스 실행 중 LockException 수정.
|
||||
- 단일 사용자 메시지에서 병렬 도구 결과 그룹화 문제 해결.
|
||||
- MCP 도구 해상도 문제 해결 및 모든 공유 가변 연결 제거.
|
||||
- human_feedback 함수에서 LLM 매개변수 처리 업데이트.
|
||||
- LockedListProxy 및 LockedDictProxy에 누락된 list/dict 메서드 추가.
|
||||
- 병렬 도구 호출 스레드에 contextvars 컨텍스트 전파.
|
||||
- CVE 경로 탐색 취약점을 해결하기 위해 gitpython 의존성을 >=3.1.41로 업데이트.
|
||||
|
||||
### 리팩토링
|
||||
- 메모리 클래스를 직렬화 가능하도록 리팩토링.
|
||||
|
||||
### 문서
|
||||
- v1.10.1에 대한 변경 로그 및 버전 업데이트.
|
||||
|
||||
## 기여자
|
||||
|
||||
@akaKuruma, @github-actions[bot], @giulio-leone, @greysonlalonde, @joaomdmoura, @jonathansampson, @lorenzejay, @lucasgomide, @mattatcha
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026년 3월 4일">
|
||||
## v1.10.1
|
||||
|
||||
|
||||
@@ -4,6 +4,39 @@ description: "Atualizações de produto, melhorias e correções do CrewAI"
|
||||
icon: "clock"
|
||||
mode: "wide"
|
||||
---
|
||||
<Update label="11 mar 2026">
|
||||
## v1.10.2a1
|
||||
|
||||
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.10.2a1)
|
||||
|
||||
## O que mudou
|
||||
|
||||
### Recursos
|
||||
- Adicionar suporte para busca de ferramentas, salvamento de tokens e injeção dinâmica de ferramentas apropriadas durante a execução para Anthropics.
|
||||
- Introduzir mais ferramentas de Busca Brave.
|
||||
- Criar ação para lançamentos noturnos.
|
||||
|
||||
### Correções de Bugs
|
||||
- Corrigir LockException durante a execução concorrente de múltiplos processos.
|
||||
- Resolver problemas com a agrupação de resultados de ferramentas paralelas em uma única mensagem de usuário.
|
||||
- Abordar resoluções de ferramentas MCP e eliminar todas as conexões mutáveis compartilhadas.
|
||||
- Atualizar o manuseio de parâmetros LLM na função human_feedback.
|
||||
- Adicionar métodos de lista/dicionário ausentes a LockedListProxy e LockedDictProxy.
|
||||
- Propagar o contexto de contextvars para as threads de chamada de ferramentas paralelas.
|
||||
- Atualizar a dependência gitpython para >=3.1.41 para resolver a vulnerabilidade de travessia de diretórios CVE.
|
||||
|
||||
### Refatoração
|
||||
- Refatorar classes de memória para serem serializáveis.
|
||||
|
||||
### Documentação
|
||||
- Atualizar o changelog e a versão para v1.10.1.
|
||||
|
||||
## Contribuidores
|
||||
|
||||
@akaKuruma, @github-actions[bot], @giulio-leone, @greysonlalonde, @joaomdmoura, @jonathansampson, @lorenzejay, @lucasgomide, @mattatcha
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="04 mar 2026">
|
||||
## v1.10.1
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
from collections.abc import Callable
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from crewai.utilities.lock_store import lock as store_lock
|
||||
from lancedb import ( # type: ignore[import-untyped]
|
||||
DBConnection as LanceDBConnection,
|
||||
connect as lancedb_connect,
|
||||
@@ -33,21 +35,24 @@ class LanceDBAdapter(Adapter):
|
||||
|
||||
_db: LanceDBConnection = PrivateAttr()
|
||||
_table: LanceDBTable = PrivateAttr()
|
||||
_lock_name: str = PrivateAttr(default="")
|
||||
|
||||
def model_post_init(self, __context: Any) -> None:
|
||||
self._db = lancedb_connect(self.uri)
|
||||
self._table = self._db.open_table(self.table_name)
|
||||
self._lock_name = f"lancedb:{os.path.realpath(str(self.uri))}"
|
||||
|
||||
super().model_post_init(__context)
|
||||
|
||||
def query(self, question: str) -> str: # type: ignore[override]
|
||||
query = self.embedding_function([question])[0]
|
||||
results = (
|
||||
self._table.search(query, vector_column_name=self.vector_column_name)
|
||||
.limit(self.top_k)
|
||||
.select([self.text_column_name])
|
||||
.to_list()
|
||||
)
|
||||
with store_lock(self._lock_name):
|
||||
results = (
|
||||
self._table.search(query, vector_column_name=self.vector_column_name)
|
||||
.limit(self.top_k)
|
||||
.select([self.text_column_name])
|
||||
.to_list()
|
||||
)
|
||||
values = [result[self.text_column_name] for result in results]
|
||||
return "\n".join(values)
|
||||
|
||||
@@ -56,4 +61,5 @@ class LanceDBAdapter(Adapter):
|
||||
*args: Any,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
self._table.add(*args, **kwargs)
|
||||
with store_lock(self._lock_name):
|
||||
self._table.add(*args, **kwargs)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import contextvars
|
||||
import logging
|
||||
import threading
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
|
||||
@@ -18,6 +21,9 @@ class BrowserSessionManager:
|
||||
This class maintains separate browser sessions for different threads,
|
||||
enabling concurrent usage of browsers in multi-threaded environments.
|
||||
Browsers are created lazily only when needed by tools.
|
||||
|
||||
Uses per-key events to serialize creation for the same thread_id without
|
||||
blocking unrelated callers or wasting resources on duplicate sessions.
|
||||
"""
|
||||
|
||||
def __init__(self, region: str = "us-west-2"):
|
||||
@@ -27,8 +33,10 @@ class BrowserSessionManager:
|
||||
region: AWS region for browser client
|
||||
"""
|
||||
self.region = region
|
||||
self._lock = threading.Lock()
|
||||
self._async_sessions: dict[str, tuple[BrowserClient, AsyncBrowser]] = {}
|
||||
self._sync_sessions: dict[str, tuple[BrowserClient, SyncBrowser]] = {}
|
||||
self._creating: dict[str, threading.Event] = {}
|
||||
|
||||
async def get_async_browser(self, thread_id: str) -> AsyncBrowser:
|
||||
"""Get or create an async browser for the specified thread.
|
||||
@@ -39,10 +47,29 @@ class BrowserSessionManager:
|
||||
Returns:
|
||||
An async browser instance specific to the thread
|
||||
"""
|
||||
if thread_id in self._async_sessions:
|
||||
return self._async_sessions[thread_id][1]
|
||||
loop = asyncio.get_event_loop()
|
||||
while True:
|
||||
with self._lock:
|
||||
if thread_id in self._async_sessions:
|
||||
return self._async_sessions[thread_id][1]
|
||||
if thread_id not in self._creating:
|
||||
self._creating[thread_id] = threading.Event()
|
||||
break
|
||||
event = self._creating[thread_id]
|
||||
ctx = contextvars.copy_context()
|
||||
await loop.run_in_executor(None, ctx.run, event.wait)
|
||||
|
||||
return await self._create_async_browser_session(thread_id)
|
||||
try:
|
||||
browser_client, browser = await self._create_async_browser_session(
|
||||
thread_id
|
||||
)
|
||||
with self._lock:
|
||||
self._async_sessions[thread_id] = (browser_client, browser)
|
||||
return browser
|
||||
finally:
|
||||
with self._lock:
|
||||
evt = self._creating.pop(thread_id)
|
||||
evt.set()
|
||||
|
||||
def get_sync_browser(self, thread_id: str) -> SyncBrowser:
|
||||
"""Get or create a sync browser for the specified thread.
|
||||
@@ -53,19 +80,33 @@ class BrowserSessionManager:
|
||||
Returns:
|
||||
A sync browser instance specific to the thread
|
||||
"""
|
||||
if thread_id in self._sync_sessions:
|
||||
return self._sync_sessions[thread_id][1]
|
||||
while True:
|
||||
with self._lock:
|
||||
if thread_id in self._sync_sessions:
|
||||
return self._sync_sessions[thread_id][1]
|
||||
if thread_id not in self._creating:
|
||||
self._creating[thread_id] = threading.Event()
|
||||
break
|
||||
event = self._creating[thread_id]
|
||||
event.wait()
|
||||
|
||||
return self._create_sync_browser_session(thread_id)
|
||||
try:
|
||||
return self._create_sync_browser_session(thread_id)
|
||||
finally:
|
||||
with self._lock:
|
||||
evt = self._creating.pop(thread_id)
|
||||
evt.set()
|
||||
|
||||
async def _create_async_browser_session(self, thread_id: str) -> AsyncBrowser:
|
||||
async def _create_async_browser_session(
|
||||
self, thread_id: str
|
||||
) -> tuple[BrowserClient, AsyncBrowser]:
|
||||
"""Create a new async browser session for the specified thread.
|
||||
|
||||
Args:
|
||||
thread_id: Unique identifier for the thread
|
||||
|
||||
Returns:
|
||||
The newly created async browser instance
|
||||
Tuple of (BrowserClient, AsyncBrowser).
|
||||
|
||||
Raises:
|
||||
Exception: If browser session creation fails
|
||||
@@ -75,10 +116,8 @@ class BrowserSessionManager:
|
||||
browser_client = BrowserClient(region=self.region)
|
||||
|
||||
try:
|
||||
# Start browser session
|
||||
browser_client.start()
|
||||
|
||||
# Get WebSocket connection info
|
||||
ws_url, headers = browser_client.generate_ws_headers()
|
||||
|
||||
logger.info(
|
||||
@@ -87,7 +126,6 @@ class BrowserSessionManager:
|
||||
|
||||
from playwright.async_api import async_playwright
|
||||
|
||||
# Connect to browser using Playwright
|
||||
playwright = await async_playwright().start()
|
||||
browser = await playwright.chromium.connect_over_cdp(
|
||||
endpoint_url=ws_url, headers=headers, timeout=30000
|
||||
@@ -96,17 +134,13 @@ class BrowserSessionManager:
|
||||
f"Successfully connected to async browser for thread {thread_id}"
|
||||
)
|
||||
|
||||
# Store session resources
|
||||
self._async_sessions[thread_id] = (browser_client, browser)
|
||||
|
||||
return browser
|
||||
return browser_client, browser
|
||||
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Failed to create async browser session for thread {thread_id}: {e}"
|
||||
)
|
||||
|
||||
# Clean up resources if session creation fails
|
||||
if browser_client:
|
||||
try:
|
||||
browser_client.stop()
|
||||
@@ -132,10 +166,8 @@ class BrowserSessionManager:
|
||||
browser_client = BrowserClient(region=self.region)
|
||||
|
||||
try:
|
||||
# Start browser session
|
||||
browser_client.start()
|
||||
|
||||
# Get WebSocket connection info
|
||||
ws_url, headers = browser_client.generate_ws_headers()
|
||||
|
||||
logger.info(
|
||||
@@ -144,7 +176,6 @@ class BrowserSessionManager:
|
||||
|
||||
from playwright.sync_api import sync_playwright
|
||||
|
||||
# Connect to browser using Playwright
|
||||
playwright = sync_playwright().start()
|
||||
browser = playwright.chromium.connect_over_cdp(
|
||||
endpoint_url=ws_url, headers=headers, timeout=30000
|
||||
@@ -153,8 +184,8 @@ class BrowserSessionManager:
|
||||
f"Successfully connected to sync browser for thread {thread_id}"
|
||||
)
|
||||
|
||||
# Store session resources
|
||||
self._sync_sessions[thread_id] = (browser_client, browser)
|
||||
with self._lock:
|
||||
self._sync_sessions[thread_id] = (browser_client, browser)
|
||||
|
||||
return browser
|
||||
|
||||
@@ -163,7 +194,6 @@ class BrowserSessionManager:
|
||||
f"Failed to create sync browser session for thread {thread_id}: {e}"
|
||||
)
|
||||
|
||||
# Clean up resources if session creation fails
|
||||
if browser_client:
|
||||
try:
|
||||
browser_client.stop()
|
||||
@@ -178,13 +208,13 @@ class BrowserSessionManager:
|
||||
Args:
|
||||
thread_id: Unique identifier for the thread
|
||||
"""
|
||||
if thread_id not in self._async_sessions:
|
||||
logger.warning(f"No async browser session found for thread {thread_id}")
|
||||
return
|
||||
with self._lock:
|
||||
if thread_id not in self._async_sessions:
|
||||
logger.warning(f"No async browser session found for thread {thread_id}")
|
||||
return
|
||||
|
||||
browser_client, browser = self._async_sessions[thread_id]
|
||||
browser_client, browser = self._async_sessions.pop(thread_id)
|
||||
|
||||
# Close browser
|
||||
if browser:
|
||||
try:
|
||||
await browser.close()
|
||||
@@ -193,7 +223,6 @@ class BrowserSessionManager:
|
||||
f"Error closing async browser for thread {thread_id}: {e}"
|
||||
)
|
||||
|
||||
# Stop browser client
|
||||
if browser_client:
|
||||
try:
|
||||
browser_client.stop()
|
||||
@@ -202,8 +231,6 @@ class BrowserSessionManager:
|
||||
f"Error stopping browser client for thread {thread_id}: {e}"
|
||||
)
|
||||
|
||||
# Remove session from dictionary
|
||||
del self._async_sessions[thread_id]
|
||||
logger.info(f"Async browser session cleaned up for thread {thread_id}")
|
||||
|
||||
def close_sync_browser(self, thread_id: str) -> None:
|
||||
@@ -212,13 +239,13 @@ class BrowserSessionManager:
|
||||
Args:
|
||||
thread_id: Unique identifier for the thread
|
||||
"""
|
||||
if thread_id not in self._sync_sessions:
|
||||
logger.warning(f"No sync browser session found for thread {thread_id}")
|
||||
return
|
||||
with self._lock:
|
||||
if thread_id not in self._sync_sessions:
|
||||
logger.warning(f"No sync browser session found for thread {thread_id}")
|
||||
return
|
||||
|
||||
browser_client, browser = self._sync_sessions[thread_id]
|
||||
browser_client, browser = self._sync_sessions.pop(thread_id)
|
||||
|
||||
# Close browser
|
||||
if browser:
|
||||
try:
|
||||
browser.close()
|
||||
@@ -227,7 +254,6 @@ class BrowserSessionManager:
|
||||
f"Error closing sync browser for thread {thread_id}: {e}"
|
||||
)
|
||||
|
||||
# Stop browser client
|
||||
if browser_client:
|
||||
try:
|
||||
browser_client.stop()
|
||||
@@ -236,19 +262,17 @@ class BrowserSessionManager:
|
||||
f"Error stopping browser client for thread {thread_id}: {e}"
|
||||
)
|
||||
|
||||
# Remove session from dictionary
|
||||
del self._sync_sessions[thread_id]
|
||||
logger.info(f"Sync browser session cleaned up for thread {thread_id}")
|
||||
|
||||
async def close_all_browsers(self) -> None:
|
||||
"""Close all browser sessions."""
|
||||
# Close all async browsers
|
||||
async_thread_ids = list(self._async_sessions.keys())
|
||||
with self._lock:
|
||||
async_thread_ids = list(self._async_sessions.keys())
|
||||
sync_thread_ids = list(self._sync_sessions.keys())
|
||||
|
||||
for thread_id in async_thread_ids:
|
||||
await self.close_async_browser(thread_id)
|
||||
|
||||
# Close all sync browsers
|
||||
sync_thread_ids = list(self._sync_sessions.keys())
|
||||
for thread_id in sync_thread_ids:
|
||||
self.close_sync_browser(thread_id)
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from uuid import uuid4
|
||||
|
||||
import chromadb
|
||||
from crewai.utilities.lock_store import lock as store_lock
|
||||
from pydantic import BaseModel, Field, PrivateAttr
|
||||
|
||||
from crewai_tools.rag.base_loader import BaseLoader
|
||||
@@ -38,22 +40,32 @@ class RAG(Adapter):
|
||||
_client: Any = PrivateAttr()
|
||||
_collection: Any = PrivateAttr()
|
||||
_embedding_service: EmbeddingService = PrivateAttr()
|
||||
_lock_name: str = PrivateAttr(default="")
|
||||
|
||||
def model_post_init(self, __context: Any) -> None:
|
||||
try:
|
||||
if self.persist_directory:
|
||||
self._client = chromadb.PersistentClient(path=self.persist_directory)
|
||||
else:
|
||||
self._client = chromadb.Client()
|
||||
|
||||
self._collection = self._client.get_or_create_collection(
|
||||
name=self.collection_name,
|
||||
metadata={
|
||||
"hnsw:space": "cosine",
|
||||
"description": "CrewAI Knowledge Base",
|
||||
},
|
||||
self._lock_name = (
|
||||
f"chromadb:{os.path.realpath(self.persist_directory)}"
|
||||
if self.persist_directory
|
||||
else "chromadb:ephemeral"
|
||||
)
|
||||
|
||||
with store_lock(self._lock_name):
|
||||
if self.persist_directory:
|
||||
self._client = chromadb.PersistentClient(
|
||||
path=self.persist_directory
|
||||
)
|
||||
else:
|
||||
self._client = chromadb.Client()
|
||||
|
||||
self._collection = self._client.get_or_create_collection(
|
||||
name=self.collection_name,
|
||||
metadata={
|
||||
"hnsw:space": "cosine",
|
||||
"description": "CrewAI Knowledge Base",
|
||||
},
|
||||
)
|
||||
|
||||
self._embedding_service = EmbeddingService(
|
||||
provider=self.embedding_provider,
|
||||
model=self.embedding_model,
|
||||
@@ -87,29 +99,8 @@ class RAG(Adapter):
|
||||
loader_result = loader.load(source_content)
|
||||
doc_id = loader_result.doc_id
|
||||
|
||||
existing_doc = self._collection.get(
|
||||
where={"source": source_content.source_ref}, limit=1
|
||||
)
|
||||
existing_doc_id = (
|
||||
existing_doc and existing_doc["metadatas"][0]["doc_id"]
|
||||
if existing_doc["metadatas"]
|
||||
else None
|
||||
)
|
||||
|
||||
if existing_doc_id == doc_id:
|
||||
logger.warning(
|
||||
f"Document with source {loader_result.source} already exists"
|
||||
)
|
||||
return
|
||||
|
||||
# Document with same source ref does exists but the content has changed, deleting the oldest reference
|
||||
if existing_doc_id and existing_doc_id != loader_result.doc_id:
|
||||
logger.warning(f"Deleting old document with doc_id {existing_doc_id}")
|
||||
self._collection.delete(where={"doc_id": existing_doc_id})
|
||||
|
||||
documents = []
|
||||
|
||||
chunks = chunker.chunk(loader_result.content)
|
||||
documents = []
|
||||
for i, chunk in enumerate(chunks):
|
||||
doc_metadata = (metadata or {}).copy()
|
||||
doc_metadata["chunk_index"] = i
|
||||
@@ -136,7 +127,6 @@ class RAG(Adapter):
|
||||
|
||||
ids = [doc.id for doc in documents]
|
||||
metadatas = []
|
||||
|
||||
for doc in documents:
|
||||
doc_metadata = doc.metadata.copy()
|
||||
doc_metadata.update(
|
||||
@@ -148,27 +138,48 @@ class RAG(Adapter):
|
||||
)
|
||||
metadatas.append(doc_metadata)
|
||||
|
||||
try:
|
||||
self._collection.add(
|
||||
ids=ids,
|
||||
embeddings=embeddings,
|
||||
documents=contents,
|
||||
metadatas=metadatas,
|
||||
with store_lock(self._lock_name):
|
||||
existing_doc = self._collection.get(
|
||||
where={"source": source_content.source_ref}, limit=1
|
||||
)
|
||||
logger.info(f"Added {len(documents)} documents to knowledge base")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to add documents to ChromaDB: {e}")
|
||||
existing_doc_id = (
|
||||
existing_doc and existing_doc["metadatas"][0]["doc_id"]
|
||||
if existing_doc["metadatas"]
|
||||
else None
|
||||
)
|
||||
|
||||
if existing_doc_id == doc_id:
|
||||
logger.warning(
|
||||
f"Document with source {loader_result.source} already exists"
|
||||
)
|
||||
return
|
||||
|
||||
if existing_doc_id and existing_doc_id != loader_result.doc_id:
|
||||
logger.warning(f"Deleting old document with doc_id {existing_doc_id}")
|
||||
self._collection.delete(where={"doc_id": existing_doc_id})
|
||||
|
||||
try:
|
||||
self._collection.add(
|
||||
ids=ids,
|
||||
embeddings=embeddings,
|
||||
documents=contents,
|
||||
metadatas=metadatas,
|
||||
)
|
||||
logger.info(f"Added {len(documents)} documents to knowledge base")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to add documents to ChromaDB: {e}")
|
||||
|
||||
def query(self, question: str, where: dict[str, Any] | None = None) -> str: # type: ignore
|
||||
try:
|
||||
question_embedding = self._embedding_service.embed_text(question)
|
||||
|
||||
results = self._collection.query(
|
||||
query_embeddings=[question_embedding],
|
||||
n_results=self.top_k,
|
||||
where=where,
|
||||
include=["documents", "metadatas", "distances"],
|
||||
)
|
||||
with store_lock(self._lock_name):
|
||||
results = self._collection.query(
|
||||
query_embeddings=[question_embedding],
|
||||
n_results=self.top_k,
|
||||
where=where,
|
||||
include=["documents", "metadatas", "distances"],
|
||||
)
|
||||
|
||||
if (
|
||||
not results
|
||||
@@ -201,7 +212,8 @@ class RAG(Adapter):
|
||||
|
||||
def delete_collection(self) -> None:
|
||||
try:
|
||||
self._client.delete_collection(self.collection_name)
|
||||
with store_lock(self._lock_name):
|
||||
self._client.delete_collection(self.collection_name)
|
||||
logger.info(f"Deleted collection: {self.collection_name}")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to delete collection: {e}")
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from datetime import datetime
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
@@ -10,8 +9,8 @@ from pydantic import BaseModel, Field
|
||||
from pydantic.types import StringConstraints
|
||||
import requests
|
||||
|
||||
from crewai_tools.tools.brave_search_tool.schemas import WebSearchParams
|
||||
from crewai_tools.tools.brave_search_tool.base import _save_results_to_file
|
||||
from crewai_tools.tools.brave_search_tool.schemas import WebSearchParams
|
||||
|
||||
|
||||
load_dotenv()
|
||||
|
||||
@@ -30,9 +30,8 @@ class FileWriterTool(BaseTool):
|
||||
|
||||
def _run(self, **kwargs: Any) -> str:
|
||||
try:
|
||||
# Create the directory if it doesn't exist
|
||||
if kwargs.get("directory") and not os.path.exists(kwargs["directory"]):
|
||||
os.makedirs(kwargs["directory"])
|
||||
if kwargs.get("directory"):
|
||||
os.makedirs(kwargs["directory"], exist_ok=True)
|
||||
|
||||
# Construct the full path
|
||||
filepath = os.path.join(kwargs.get("directory") or "", kwargs["filename"])
|
||||
|
||||
@@ -99,8 +99,8 @@ class FileCompressorTool(BaseTool):
|
||||
def _prepare_output(output_path: str, overwrite: bool) -> bool:
|
||||
"""Ensures output path is ready for writing."""
|
||||
output_dir = os.path.dirname(output_path)
|
||||
if output_dir and not os.path.exists(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
if output_dir:
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
if os.path.exists(output_path) and not overwrite:
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -18,7 +18,6 @@ class MergeAgentHandlerToolError(Exception):
|
||||
"""Base exception for Merge Agent Handler tool errors."""
|
||||
|
||||
|
||||
|
||||
class MergeAgentHandlerTool(BaseTool):
|
||||
"""
|
||||
Wrapper for Merge Agent Handler tools.
|
||||
@@ -174,7 +173,7 @@ class MergeAgentHandlerTool(BaseTool):
|
||||
>>> tool = MergeAgentHandlerTool.from_tool_name(
|
||||
... tool_name="linear__create_issue",
|
||||
... tool_pack_id="134e0111-0f67-44f6-98f0-597000290bb3",
|
||||
... registered_user_id="91b2b905-e866-40c8-8be2-efe53827a0aa"
|
||||
... registered_user_id="91b2b905-e866-40c8-8be2-efe53827a0aa",
|
||||
... )
|
||||
"""
|
||||
# Create an empty args schema model (proper BaseModel subclass)
|
||||
@@ -210,7 +209,10 @@ class MergeAgentHandlerTool(BaseTool):
|
||||
if "parameters" in tool_schema:
|
||||
try:
|
||||
params = tool_schema["parameters"]
|
||||
if params.get("type") == "object" and "properties" in params:
|
||||
if (
|
||||
params.get("type") == "object"
|
||||
and "properties" in params
|
||||
):
|
||||
# Build field definitions for Pydantic
|
||||
fields = {}
|
||||
properties = params["properties"]
|
||||
@@ -298,7 +300,7 @@ class MergeAgentHandlerTool(BaseTool):
|
||||
>>> tools = MergeAgentHandlerTool.from_tool_pack(
|
||||
... tool_pack_id="134e0111-0f67-44f6-98f0-597000290bb3",
|
||||
... registered_user_id="91b2b905-e866-40c8-8be2-efe53827a0aa",
|
||||
... tool_names=["linear__create_issue", "linear__get_issues"]
|
||||
... tool_names=["linear__create_issue", "linear__get_issues"],
|
||||
... )
|
||||
"""
|
||||
# Create a temporary instance to fetch the tool list
|
||||
|
||||
@@ -110,11 +110,13 @@ class QdrantVectorSearchTool(BaseTool):
|
||||
self.custom_embedding_fn(query)
|
||||
if self.custom_embedding_fn
|
||||
else (
|
||||
lambda: __import__("openai")
|
||||
.Client(api_key=os.getenv("OPENAI_API_KEY"))
|
||||
.embeddings.create(input=[query], model="text-embedding-3-large")
|
||||
.data[0]
|
||||
.embedding
|
||||
lambda: (
|
||||
__import__("openai")
|
||||
.Client(api_key=os.getenv("OPENAI_API_KEY"))
|
||||
.embeddings.create(input=[query], model="text-embedding-3-large")
|
||||
.data[0]
|
||||
.embedding
|
||||
)
|
||||
)()
|
||||
)
|
||||
results = self.client.query_points(
|
||||
|
||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
import logging
|
||||
import threading
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
from crewai.tools.base_tool import BaseTool
|
||||
@@ -33,6 +34,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
# Cache for query results
|
||||
_query_cache: dict[str, list[dict[str, Any]]] = {}
|
||||
_cache_lock = threading.Lock()
|
||||
|
||||
|
||||
class SnowflakeConfig(BaseModel):
|
||||
@@ -102,7 +104,7 @@ class SnowflakeSearchTool(BaseTool):
|
||||
)
|
||||
|
||||
_connection_pool: list[SnowflakeConnection] | None = None
|
||||
_pool_lock: asyncio.Lock | None = None
|
||||
_pool_lock: threading.Lock | None = None
|
||||
_thread_pool: ThreadPoolExecutor | None = None
|
||||
_model_rebuilt: bool = False
|
||||
package_dependencies: list[str] = Field(
|
||||
@@ -122,7 +124,7 @@ class SnowflakeSearchTool(BaseTool):
|
||||
try:
|
||||
if SNOWFLAKE_AVAILABLE:
|
||||
self._connection_pool = []
|
||||
self._pool_lock = asyncio.Lock()
|
||||
self._pool_lock = threading.Lock()
|
||||
self._thread_pool = ThreadPoolExecutor(max_workers=self.pool_size)
|
||||
else:
|
||||
raise ImportError
|
||||
@@ -147,7 +149,7 @@ class SnowflakeSearchTool(BaseTool):
|
||||
)
|
||||
|
||||
self._connection_pool = []
|
||||
self._pool_lock = asyncio.Lock()
|
||||
self._pool_lock = threading.Lock()
|
||||
self._thread_pool = ThreadPoolExecutor(max_workers=self.pool_size)
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise ImportError("Failed to install Snowflake dependencies") from e
|
||||
@@ -163,13 +165,12 @@ class SnowflakeSearchTool(BaseTool):
|
||||
raise RuntimeError("Pool lock not initialized")
|
||||
if self._connection_pool is None:
|
||||
raise RuntimeError("Connection pool not initialized")
|
||||
async with self._pool_lock:
|
||||
if not self._connection_pool:
|
||||
conn = await asyncio.get_event_loop().run_in_executor(
|
||||
self._thread_pool, self._create_connection
|
||||
)
|
||||
self._connection_pool.append(conn)
|
||||
return self._connection_pool.pop()
|
||||
with self._pool_lock:
|
||||
if self._connection_pool:
|
||||
return self._connection_pool.pop()
|
||||
return await asyncio.get_event_loop().run_in_executor(
|
||||
self._thread_pool, self._create_connection
|
||||
)
|
||||
|
||||
def _create_connection(self) -> SnowflakeConnection:
|
||||
"""Create a new Snowflake connection."""
|
||||
@@ -204,9 +205,10 @@ class SnowflakeSearchTool(BaseTool):
|
||||
"""Execute a query with retries and return results."""
|
||||
if self.enable_caching:
|
||||
cache_key = self._get_cache_key(query, timeout)
|
||||
if cache_key in _query_cache:
|
||||
logger.info("Returning cached result")
|
||||
return _query_cache[cache_key]
|
||||
with _cache_lock:
|
||||
if cache_key in _query_cache:
|
||||
logger.info("Returning cached result")
|
||||
return _query_cache[cache_key]
|
||||
|
||||
for attempt in range(self.max_retries):
|
||||
try:
|
||||
@@ -225,7 +227,8 @@ class SnowflakeSearchTool(BaseTool):
|
||||
]
|
||||
|
||||
if self.enable_caching:
|
||||
_query_cache[self._get_cache_key(query, timeout)] = results
|
||||
with _cache_lock:
|
||||
_query_cache[self._get_cache_key(query, timeout)] = results
|
||||
|
||||
return results
|
||||
finally:
|
||||
@@ -234,7 +237,7 @@ class SnowflakeSearchTool(BaseTool):
|
||||
self._pool_lock is not None
|
||||
and self._connection_pool is not None
|
||||
):
|
||||
async with self._pool_lock:
|
||||
with self._pool_lock:
|
||||
self._connection_pool.append(conn)
|
||||
except (DatabaseError, OperationalError) as e: # noqa: PERF203
|
||||
if attempt == self.max_retries - 1:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import asyncio
|
||||
import contextvars
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
@@ -137,7 +138,9 @@ class StagehandTool(BaseTool):
|
||||
- 'observe': For finding elements in a specific area
|
||||
"""
|
||||
args_schema: type[BaseModel] = StagehandToolSchema
|
||||
package_dependencies: list[str] = Field(default_factory=lambda: ["stagehand<=0.5.9"])
|
||||
package_dependencies: list[str] = Field(
|
||||
default_factory=lambda: ["stagehand<=0.5.9"]
|
||||
)
|
||||
env_vars: list[EnvVar] = Field(
|
||||
default_factory=lambda: [
|
||||
EnvVar(
|
||||
@@ -620,9 +623,12 @@ class StagehandTool(BaseTool):
|
||||
# We're in an existing event loop, use it
|
||||
import concurrent.futures
|
||||
|
||||
ctx = contextvars.copy_context()
|
||||
with concurrent.futures.ThreadPoolExecutor() as executor:
|
||||
future = executor.submit(
|
||||
asyncio.run, self._async_run(instruction, url, command_type)
|
||||
ctx.run,
|
||||
asyncio.run,
|
||||
self._async_run(instruction, url, command_type),
|
||||
)
|
||||
result = future.result()
|
||||
else:
|
||||
@@ -706,11 +712,12 @@ class StagehandTool(BaseTool):
|
||||
if loop.is_running():
|
||||
import concurrent.futures
|
||||
|
||||
ctx = contextvars.copy_context()
|
||||
with (
|
||||
concurrent.futures.ThreadPoolExecutor() as executor
|
||||
):
|
||||
future = executor.submit(
|
||||
asyncio.run, self._async_close()
|
||||
ctx.run, asyncio.run, self._async_close()
|
||||
)
|
||||
future.result()
|
||||
else:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import contextvars
|
||||
import threading
|
||||
from typing import Any
|
||||
import urllib.request
|
||||
@@ -66,7 +67,8 @@ def _track_install() -> None:
|
||||
def _track_install_async() -> None:
|
||||
"""Track installation in background thread to avoid blocking imports."""
|
||||
if not Telemetry._is_telemetry_disabled():
|
||||
thread = threading.Thread(target=_track_install, daemon=True)
|
||||
ctx = contextvars.copy_context()
|
||||
thread = threading.Thread(target=ctx.run, args=(_track_install,), daemon=True)
|
||||
thread.start()
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
from collections.abc import MutableMapping
|
||||
import concurrent.futures
|
||||
import contextvars
|
||||
from functools import lru_cache
|
||||
import ssl
|
||||
import time
|
||||
@@ -147,8 +148,9 @@ def fetch_agent_card(
|
||||
has_running_loop = False
|
||||
|
||||
if has_running_loop:
|
||||
ctx = contextvars.copy_context()
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
|
||||
return pool.submit(asyncio.run, coro).result()
|
||||
return pool.submit(ctx.run, asyncio.run, coro).result()
|
||||
return asyncio.run(coro)
|
||||
|
||||
|
||||
@@ -215,8 +217,9 @@ def _fetch_agent_card_cached(
|
||||
has_running_loop = False
|
||||
|
||||
if has_running_loop:
|
||||
ctx = contextvars.copy_context()
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
|
||||
return pool.submit(asyncio.run, coro).result()
|
||||
return pool.submit(ctx.run, asyncio.run, coro).result()
|
||||
return asyncio.run(coro)
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import base64
|
||||
from collections.abc import AsyncIterator, Callable, MutableMapping
|
||||
import concurrent.futures
|
||||
from contextlib import asynccontextmanager
|
||||
import contextvars
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any, Final, Literal
|
||||
import uuid
|
||||
@@ -229,8 +230,9 @@ def execute_a2a_delegation(
|
||||
has_running_loop = False
|
||||
|
||||
if has_running_loop:
|
||||
ctx = contextvars.copy_context()
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
|
||||
return pool.submit(asyncio.run, coro).result()
|
||||
return pool.submit(ctx.run, asyncio.run, coro).result()
|
||||
return asyncio.run(coro)
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
from collections.abc import Callable, Coroutine, Mapping
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
import contextvars
|
||||
from functools import wraps
|
||||
import json
|
||||
from types import MethodType
|
||||
@@ -278,7 +279,9 @@ def _fetch_agent_cards_concurrently(
|
||||
max_workers = min(len(a2a_agents), 10)
|
||||
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
||||
futures = {
|
||||
executor.submit(_fetch_card_from_config, config): config
|
||||
executor.submit(
|
||||
contextvars.copy_context().run, _fetch_card_from_config, config
|
||||
): config
|
||||
for config in a2a_agents
|
||||
}
|
||||
for future in as_completed(futures):
|
||||
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Callable, Coroutine, Sequence
|
||||
import contextvars
|
||||
import shutil
|
||||
import subprocess
|
||||
import time
|
||||
@@ -513,9 +514,13 @@ class Agent(BaseAgent):
|
||||
"""
|
||||
import concurrent.futures
|
||||
|
||||
ctx = contextvars.copy_context()
|
||||
with concurrent.futures.ThreadPoolExecutor() as executor:
|
||||
future = executor.submit(
|
||||
self._execute_without_timeout, task_prompt=task_prompt, task=task
|
||||
ctx.run,
|
||||
self._execute_without_timeout,
|
||||
task_prompt=task_prompt,
|
||||
task=task,
|
||||
)
|
||||
|
||||
try:
|
||||
|
||||
@@ -895,7 +895,9 @@ class CrewAgentExecutor(CrewAgentExecutorMixin):
|
||||
ToolUsageStartedEvent,
|
||||
)
|
||||
|
||||
args_dict, parse_error = parse_tool_call_args(func_args, func_name, call_id, original_tool)
|
||||
args_dict, parse_error = parse_tool_call_args(
|
||||
func_args, func_name, call_id, original_tool
|
||||
)
|
||||
if parse_error is not None:
|
||||
return parse_error
|
||||
|
||||
|
||||
@@ -182,15 +182,24 @@ def log_tasks_outputs() -> None:
|
||||
@crewai.command()
|
||||
@click.option("-m", "--memory", is_flag=True, help="Reset MEMORY")
|
||||
@click.option(
|
||||
"-l", "--long", is_flag=True, hidden=True,
|
||||
"-l",
|
||||
"--long",
|
||||
is_flag=True,
|
||||
hidden=True,
|
||||
help="[Deprecated: use --memory] Reset memory",
|
||||
)
|
||||
@click.option(
|
||||
"-s", "--short", is_flag=True, hidden=True,
|
||||
"-s",
|
||||
"--short",
|
||||
is_flag=True,
|
||||
hidden=True,
|
||||
help="[Deprecated: use --memory] Reset memory",
|
||||
)
|
||||
@click.option(
|
||||
"-e", "--entities", is_flag=True, hidden=True,
|
||||
"-e",
|
||||
"--entities",
|
||||
is_flag=True,
|
||||
hidden=True,
|
||||
help="[Deprecated: use --memory] Reset memory",
|
||||
)
|
||||
@click.option("-kn", "--knowledge", is_flag=True, help="Reset KNOWLEDGE storage")
|
||||
@@ -218,7 +227,13 @@ def reset_memories(
|
||||
# Treat legacy flags as --memory with a deprecation warning
|
||||
if long or short or entities:
|
||||
legacy_used = [
|
||||
f for f, v in [("--long", long), ("--short", short), ("--entities", entities)] if v
|
||||
f
|
||||
for f, v in [
|
||||
("--long", long),
|
||||
("--short", short),
|
||||
("--entities", entities),
|
||||
]
|
||||
if v
|
||||
]
|
||||
click.echo(
|
||||
f"Warning: {', '.join(legacy_used)} {'is' if len(legacy_used) == 1 else 'are'} "
|
||||
@@ -238,9 +253,7 @@ def reset_memories(
|
||||
"Please specify at least one memory type to reset using the appropriate flags."
|
||||
)
|
||||
return
|
||||
reset_memories_command(
|
||||
memory, knowledge, agent_knowledge, kickoff_outputs, all
|
||||
)
|
||||
reset_memories_command(memory, knowledge, agent_knowledge, kickoff_outputs, all)
|
||||
except Exception as e:
|
||||
click.echo(f"An error occurred while resetting memories: {e}", err=True)
|
||||
|
||||
@@ -669,18 +682,11 @@ def traces_enable():
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
|
||||
from crewai.events.listeners.tracing.utils import (
|
||||
_load_user_data,
|
||||
_save_user_data,
|
||||
)
|
||||
from crewai.events.listeners.tracing.utils import update_user_data
|
||||
|
||||
console = Console()
|
||||
|
||||
# Update user data to enable traces
|
||||
user_data = _load_user_data()
|
||||
user_data["trace_consent"] = True
|
||||
user_data["first_execution_done"] = True
|
||||
_save_user_data(user_data)
|
||||
update_user_data({"trace_consent": True, "first_execution_done": True})
|
||||
|
||||
panel = Panel(
|
||||
"✅ Trace collection has been enabled!\n\n"
|
||||
@@ -699,18 +705,11 @@ def traces_disable():
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
|
||||
from crewai.events.listeners.tracing.utils import (
|
||||
_load_user_data,
|
||||
_save_user_data,
|
||||
)
|
||||
from crewai.events.listeners.tracing.utils import update_user_data
|
||||
|
||||
console = Console()
|
||||
|
||||
# Update user data to disable traces
|
||||
user_data = _load_user_data()
|
||||
user_data["trace_consent"] = False
|
||||
user_data["first_execution_done"] = True
|
||||
_save_user_data(user_data)
|
||||
update_user_data({"trace_consent": False, "first_execution_done": True})
|
||||
|
||||
panel = Panel(
|
||||
"❌ Trace collection has been disabled!\n\n"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import contextvars
|
||||
import json
|
||||
from pathlib import Path
|
||||
import platform
|
||||
@@ -80,7 +81,10 @@ def run_chat() -> None:
|
||||
|
||||
# Start loading indicator
|
||||
loading_complete = threading.Event()
|
||||
loading_thread = threading.Thread(target=show_loading, args=(loading_complete,))
|
||||
ctx = contextvars.copy_context()
|
||||
loading_thread = threading.Thread(
|
||||
target=ctx.run, args=(show_loading, loading_complete)
|
||||
)
|
||||
loading_thread.start()
|
||||
|
||||
try:
|
||||
|
||||
@@ -125,13 +125,19 @@ class MemoryTUI(App[None]):
|
||||
from crewai.memory.storage.lancedb_storage import LanceDBStorage
|
||||
from crewai.memory.unified_memory import Memory
|
||||
|
||||
storage = LanceDBStorage(path=storage_path) if storage_path else LanceDBStorage()
|
||||
storage = (
|
||||
LanceDBStorage(path=storage_path) if storage_path else LanceDBStorage()
|
||||
)
|
||||
embedder = None
|
||||
if embedder_config is not None:
|
||||
from crewai.rag.embeddings.factory import build_embedder
|
||||
|
||||
embedder = build_embedder(embedder_config)
|
||||
self._memory = Memory(storage=storage, embedder=embedder) if embedder else Memory(storage=storage)
|
||||
self._memory = (
|
||||
Memory(storage=storage, embedder=embedder)
|
||||
if embedder
|
||||
else Memory(storage=storage)
|
||||
)
|
||||
except Exception as e:
|
||||
self._init_error = str(e)
|
||||
|
||||
@@ -200,11 +206,7 @@ class MemoryTUI(App[None]):
|
||||
if len(record.content) > 80
|
||||
else record.content
|
||||
)
|
||||
label = (
|
||||
f"{date_str} "
|
||||
f"[bold]{record.importance:.1f}[/] "
|
||||
f"{preview}"
|
||||
)
|
||||
label = f"{date_str} [bold]{record.importance:.1f}[/] {preview}"
|
||||
option_list.add_option(label)
|
||||
|
||||
def _populate_recall_list(self) -> None:
|
||||
@@ -220,9 +222,7 @@ class MemoryTUI(App[None]):
|
||||
else m.record.content
|
||||
)
|
||||
label = (
|
||||
f"[bold]\\[{m.score:.2f}][/] "
|
||||
f"{preview} "
|
||||
f"[dim]scope={m.record.scope}[/]"
|
||||
f"[bold]\\[{m.score:.2f}][/] {preview} [dim]scope={m.record.scope}[/]"
|
||||
)
|
||||
option_list.add_option(label)
|
||||
|
||||
@@ -251,8 +251,7 @@ class MemoryTUI(App[None]):
|
||||
lines.append(f"[dim]Scope:[/] [bold]{record.scope}[/]")
|
||||
lines.append(f"[dim]Importance:[/] [bold]{record.importance:.2f}[/]")
|
||||
lines.append(
|
||||
f"[dim]Created:[/] "
|
||||
f"{record.created_at.strftime('%Y-%m-%d %H:%M:%S')}"
|
||||
f"[dim]Created:[/] {record.created_at.strftime('%Y-%m-%d %H:%M:%S')}"
|
||||
)
|
||||
lines.append(
|
||||
f"[dim]Last accessed:[/] "
|
||||
@@ -362,17 +361,11 @@ class MemoryTUI(App[None]):
|
||||
panel = self.query_one("#info-panel", Static)
|
||||
panel.loading = True
|
||||
try:
|
||||
scope = (
|
||||
self._selected_scope
|
||||
if self._selected_scope != "/"
|
||||
else None
|
||||
)
|
||||
scope = self._selected_scope if self._selected_scope != "/" else None
|
||||
loop = asyncio.get_event_loop()
|
||||
matches = await loop.run_in_executor(
|
||||
None,
|
||||
lambda: self._memory.recall(
|
||||
query, scope=scope, limit=10, depth="deep"
|
||||
),
|
||||
lambda: self._memory.recall(query, scope=scope, limit=10, depth="deep"),
|
||||
)
|
||||
self._recall_matches = matches or []
|
||||
self._view_mode = "recall"
|
||||
|
||||
@@ -95,9 +95,7 @@ def reset_memories_command(
|
||||
continue
|
||||
if memory:
|
||||
_reset_flow_memory(flow)
|
||||
click.echo(
|
||||
f"[Flow ({flow_name})] Memory has been reset."
|
||||
)
|
||||
click.echo(f"[Flow ({flow_name})] Memory has been reset.")
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
click.echo(f"An error occurred while resetting the memories: {e}", err=True)
|
||||
|
||||
@@ -442,9 +442,7 @@ def get_flows(flow_path: str = "main.py") -> list[Flow]:
|
||||
for search_path in search_paths:
|
||||
for root, dirs, files in os.walk(search_path):
|
||||
dirs[:] = [
|
||||
d
|
||||
for d in dirs
|
||||
if d not in _SKIP_DIRS and not d.startswith(".")
|
||||
d for d in dirs if d not in _SKIP_DIRS and not d.startswith(".")
|
||||
]
|
||||
if flow_path in files and "cli/templates" not in root:
|
||||
file_os_path = os.path.join(root, flow_path)
|
||||
@@ -464,9 +462,7 @@ def get_flows(flow_path: str = "main.py") -> list[Flow]:
|
||||
for attr_name in dir(module):
|
||||
module_attr = getattr(module, attr_name)
|
||||
try:
|
||||
if flow_instance := get_flow_instance(
|
||||
module_attr
|
||||
):
|
||||
if flow_instance := get_flow_instance(module_attr):
|
||||
flow_instances.append(flow_instance)
|
||||
except Exception: # noqa: S112
|
||||
continue
|
||||
|
||||
@@ -1410,9 +1410,7 @@ class Crew(FlowTrackable, BaseModel):
|
||||
return self._merge_tools(tools, cast(list[BaseTool], code_tools))
|
||||
return tools
|
||||
|
||||
def _add_memory_tools(
|
||||
self, tools: list[BaseTool], memory: Any
|
||||
) -> list[BaseTool]:
|
||||
def _add_memory_tools(self, tools: list[BaseTool], memory: Any) -> list[BaseTool]:
|
||||
"""Add recall and remember tools when memory is available.
|
||||
|
||||
Args:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from collections.abc import Callable
|
||||
import contextvars
|
||||
from contextvars import ContextVar, Token
|
||||
from datetime import datetime
|
||||
import getpass
|
||||
@@ -18,6 +19,7 @@ from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
from rich.text import Text
|
||||
|
||||
from crewai.utilities.lock_store import lock as store_lock
|
||||
from crewai.utilities.paths import db_storage_path
|
||||
from crewai.utilities.serialization import to_serializable
|
||||
|
||||
@@ -137,12 +139,25 @@ def _load_user_data() -> dict[str, Any]:
|
||||
return {}
|
||||
|
||||
|
||||
def _save_user_data(data: dict[str, Any]) -> None:
|
||||
def _user_data_lock_name() -> str:
|
||||
"""Return a stable lock name for the user data file."""
|
||||
return f"file:{os.path.realpath(_user_data_file())}"
|
||||
|
||||
|
||||
def update_user_data(updates: dict[str, Any]) -> None:
|
||||
"""Atomically read-modify-write the user data file.
|
||||
|
||||
Args:
|
||||
updates: Key-value pairs to merge into the existing user data.
|
||||
"""
|
||||
try:
|
||||
p = _user_data_file()
|
||||
p.write_text(json.dumps(data, indent=2))
|
||||
with store_lock(_user_data_lock_name()):
|
||||
data = _load_user_data()
|
||||
data.update(updates)
|
||||
p = _user_data_file()
|
||||
p.write_text(json.dumps(data, indent=2))
|
||||
except (OSError, PermissionError) as e:
|
||||
logger.warning(f"Failed to save user data: {e}")
|
||||
logger.warning(f"Failed to update user data: {e}")
|
||||
|
||||
|
||||
def has_user_declined_tracing() -> bool:
|
||||
@@ -357,24 +372,30 @@ def _get_generic_system_id() -> str | None:
|
||||
return None
|
||||
|
||||
|
||||
def get_user_id() -> str:
|
||||
"""Stable, anonymized user identifier with caching."""
|
||||
data = _load_user_data()
|
||||
|
||||
if "user_id" in data:
|
||||
return cast(str, data["user_id"])
|
||||
|
||||
def _generate_user_id() -> str:
|
||||
"""Compute an anonymized user identifier from username and machine ID."""
|
||||
try:
|
||||
username = getpass.getuser()
|
||||
except Exception:
|
||||
username = "unknown"
|
||||
|
||||
seed = f"{username}|{_get_machine_id()}"
|
||||
uid = hashlib.sha256(seed.encode()).hexdigest()
|
||||
return hashlib.sha256(seed.encode()).hexdigest()
|
||||
|
||||
data["user_id"] = uid
|
||||
_save_user_data(data)
|
||||
return uid
|
||||
|
||||
def get_user_id() -> str:
|
||||
"""Stable, anonymized user identifier with caching."""
|
||||
with store_lock(_user_data_lock_name()):
|
||||
data = _load_user_data()
|
||||
|
||||
if "user_id" in data:
|
||||
return cast(str, data["user_id"])
|
||||
|
||||
uid = _generate_user_id()
|
||||
data["user_id"] = uid
|
||||
p = _user_data_file()
|
||||
p.write_text(json.dumps(data, indent=2))
|
||||
return uid
|
||||
|
||||
|
||||
def is_first_execution() -> bool:
|
||||
@@ -389,20 +410,23 @@ def mark_first_execution_done(user_consented: bool = False) -> None:
|
||||
Args:
|
||||
user_consented: Whether the user consented to trace collection.
|
||||
"""
|
||||
data = _load_user_data()
|
||||
if data.get("first_execution_done", False):
|
||||
return
|
||||
with store_lock(_user_data_lock_name()):
|
||||
data = _load_user_data()
|
||||
if data.get("first_execution_done", False):
|
||||
return
|
||||
|
||||
data.update(
|
||||
{
|
||||
"first_execution_done": True,
|
||||
"first_execution_at": datetime.now().timestamp(),
|
||||
"user_id": get_user_id(),
|
||||
"machine_id": _get_machine_id(),
|
||||
"trace_consent": user_consented,
|
||||
}
|
||||
)
|
||||
_save_user_data(data)
|
||||
uid = data.get("user_id") or _generate_user_id()
|
||||
data.update(
|
||||
{
|
||||
"first_execution_done": True,
|
||||
"first_execution_at": datetime.now().timestamp(),
|
||||
"user_id": uid,
|
||||
"machine_id": _get_machine_id(),
|
||||
"trace_consent": user_consented,
|
||||
}
|
||||
)
|
||||
p = _user_data_file()
|
||||
p.write_text(json.dumps(data, indent=2))
|
||||
|
||||
|
||||
def safe_serialize_to_dict(obj: Any, exclude: set[str] | None = None) -> dict[str, Any]:
|
||||
@@ -509,7 +533,8 @@ def prompt_user_for_trace_viewing(timeout_seconds: int = 20) -> bool:
|
||||
# Handle all input-related errors silently
|
||||
result[0] = False
|
||||
|
||||
input_thread = threading.Thread(target=get_input, daemon=True)
|
||||
ctx = contextvars.copy_context()
|
||||
input_thread = threading.Thread(target=ctx.run, args=(get_input,), daemon=True)
|
||||
input_thread.start()
|
||||
input_thread.join(timeout=timeout_seconds)
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ def should_suppress_console_output() -> bool:
|
||||
|
||||
class ConsoleFormatter:
|
||||
tool_usage_counts: ClassVar[dict[str, int]] = {}
|
||||
_tool_counts_lock: ClassVar[threading.Lock] = threading.Lock()
|
||||
|
||||
current_a2a_turn_count: int = 0
|
||||
_pending_a2a_message: str | None = None
|
||||
@@ -445,9 +446,11 @@ To enable tracing, do any one of these:
|
||||
if not self.verbose:
|
||||
return
|
||||
|
||||
# Update tool usage count
|
||||
self.tool_usage_counts[tool_name] = self.tool_usage_counts.get(tool_name, 0) + 1
|
||||
iteration = self.tool_usage_counts[tool_name]
|
||||
with self._tool_counts_lock:
|
||||
self.tool_usage_counts[tool_name] = (
|
||||
self.tool_usage_counts.get(tool_name, 0) + 1
|
||||
)
|
||||
iteration = self.tool_usage_counts[tool_name]
|
||||
|
||||
content = Text()
|
||||
content.append("Tool: ", style="white")
|
||||
@@ -474,7 +477,8 @@ To enable tracing, do any one of these:
|
||||
if not self.verbose:
|
||||
return
|
||||
|
||||
iteration = self.tool_usage_counts.get(tool_name, 1)
|
||||
with self._tool_counts_lock:
|
||||
iteration = self.tool_usage_counts.get(tool_name, 1)
|
||||
|
||||
content = Text()
|
||||
content.append("Tool Completed\n", style="green bold")
|
||||
@@ -500,7 +504,8 @@ To enable tracing, do any one of these:
|
||||
if not self.verbose:
|
||||
return
|
||||
|
||||
iteration = self.tool_usage_counts.get(tool_name, 1)
|
||||
with self._tool_counts_lock:
|
||||
iteration = self.tool_usage_counts.get(tool_name, 1)
|
||||
|
||||
content = Text()
|
||||
content.append("Tool Failed\n", style="red bold")
|
||||
|
||||
@@ -729,7 +729,11 @@ class AgentExecutor(Flow[AgentReActState], CrewAgentExecutorMixin):
|
||||
max_workers = min(8, len(runnable_tool_calls))
|
||||
with ThreadPoolExecutor(max_workers=max_workers) as pool:
|
||||
future_to_idx = {
|
||||
pool.submit(contextvars.copy_context().run, self._execute_single_native_tool_call, tool_call): idx
|
||||
pool.submit(
|
||||
contextvars.copy_context().run,
|
||||
self._execute_single_native_tool_call,
|
||||
tool_call,
|
||||
): idx
|
||||
for idx, tool_call in enumerate(runnable_tool_calls)
|
||||
}
|
||||
ordered_results: list[dict[str, Any] | None] = [None] * len(
|
||||
|
||||
@@ -34,6 +34,7 @@ class ConsoleProvider:
|
||||
```python
|
||||
from crewai.flow.async_feedback import ConsoleProvider
|
||||
|
||||
|
||||
@human_feedback(
|
||||
message="Review this:",
|
||||
provider=ConsoleProvider(),
|
||||
@@ -46,6 +47,7 @@ class ConsoleProvider:
|
||||
```python
|
||||
from crewai.flow import Flow, start
|
||||
|
||||
|
||||
class MyFlow(Flow):
|
||||
@start()
|
||||
def gather_info(self):
|
||||
|
||||
@@ -17,6 +17,7 @@ from collections.abc import (
|
||||
ValuesView,
|
||||
)
|
||||
from concurrent.futures import Future, ThreadPoolExecutor
|
||||
import contextvars
|
||||
import copy
|
||||
import enum
|
||||
import inspect
|
||||
@@ -497,7 +498,9 @@ class LockedListProxy(list, Generic[T]): # type: ignore[type-arg]
|
||||
def __bool__(self) -> bool:
|
||||
return bool(self._list)
|
||||
|
||||
def index(self, value: T, start: SupportsIndex = 0, stop: SupportsIndex | None = None) -> int: # type: ignore[override]
|
||||
def index(
|
||||
self, value: T, start: SupportsIndex = 0, stop: SupportsIndex | None = None
|
||||
) -> int: # type: ignore[override]
|
||||
if stop is None:
|
||||
return self._list.index(value, start)
|
||||
return self._list.index(value, start, stop)
|
||||
@@ -1811,8 +1814,9 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
||||
|
||||
try:
|
||||
asyncio.get_running_loop()
|
||||
ctx = contextvars.copy_context()
|
||||
with ThreadPoolExecutor(max_workers=1) as pool:
|
||||
return pool.submit(asyncio.run, _run_flow()).result()
|
||||
return pool.submit(ctx.run, asyncio.run, _run_flow()).result()
|
||||
except RuntimeError:
|
||||
return asyncio.run(_run_flow())
|
||||
|
||||
@@ -2236,8 +2240,6 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
||||
else:
|
||||
# Run sync methods in thread pool for isolation
|
||||
# This allows Agent.kickoff() to work synchronously inside Flow methods
|
||||
import contextvars
|
||||
|
||||
ctx = contextvars.copy_context()
|
||||
result = await asyncio.to_thread(ctx.run, method, *args, **kwargs)
|
||||
finally:
|
||||
@@ -2856,8 +2858,9 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
||||
# Manual executor management to avoid shutdown(wait=True)
|
||||
# deadlock when the provider call outlives the timeout.
|
||||
executor = ThreadPoolExecutor(max_workers=1)
|
||||
ctx = contextvars.copy_context()
|
||||
future = executor.submit(
|
||||
provider.request_input, message, self, metadata
|
||||
ctx.run, provider.request_input, message, self, metadata
|
||||
)
|
||||
try:
|
||||
raw = future.result(timeout=timeout)
|
||||
|
||||
@@ -188,7 +188,7 @@ def human_feedback(
|
||||
metadata: dict[str, Any] | None = None,
|
||||
provider: HumanFeedbackProvider | None = None,
|
||||
learn: bool = False,
|
||||
learn_source: str = "hitl"
|
||||
learn_source: str = "hitl",
|
||||
) -> Callable[[F], F]:
|
||||
"""Decorator for Flow methods that require human feedback.
|
||||
|
||||
@@ -328,9 +328,7 @@ def human_feedback(
|
||||
"""Recall past HITL lessons and use LLM to pre-review the output."""
|
||||
try:
|
||||
query = f"human feedback lessons for {func.__name__}: {method_output!s}"
|
||||
matches = flow_instance.memory.recall(
|
||||
query, source=learn_source
|
||||
)
|
||||
matches = flow_instance.memory.recall(query, source=learn_source)
|
||||
if not matches:
|
||||
return method_output
|
||||
|
||||
@@ -341,7 +339,10 @@ def human_feedback(
|
||||
lessons=lessons,
|
||||
)
|
||||
messages = [
|
||||
{"role": "system", "content": _get_hitl_prompt("hitl_pre_review_system")},
|
||||
{
|
||||
"role": "system",
|
||||
"content": _get_hitl_prompt("hitl_pre_review_system"),
|
||||
},
|
||||
{"role": "user", "content": prompt},
|
||||
]
|
||||
if getattr(llm_inst, "supports_function_calling", lambda: False)():
|
||||
@@ -366,7 +367,10 @@ def human_feedback(
|
||||
feedback=raw_feedback,
|
||||
)
|
||||
messages = [
|
||||
{"role": "system", "content": _get_hitl_prompt("hitl_distill_system")},
|
||||
{
|
||||
"role": "system",
|
||||
"content": _get_hitl_prompt("hitl_distill_system"),
|
||||
},
|
||||
{"role": "user", "content": prompt},
|
||||
]
|
||||
|
||||
@@ -487,7 +491,11 @@ def human_feedback(
|
||||
result = _process_feedback(self, method_output, raw_feedback)
|
||||
|
||||
# Distill: extract lessons from output + feedback, store in memory
|
||||
if learn and getattr(self, "memory", None) is not None and raw_feedback.strip():
|
||||
if (
|
||||
learn
|
||||
and getattr(self, "memory", None) is not None
|
||||
and raw_feedback.strip()
|
||||
):
|
||||
_distill_and_store_lessons(self, method_output, raw_feedback)
|
||||
|
||||
return result
|
||||
@@ -507,7 +515,11 @@ def human_feedback(
|
||||
result = _process_feedback(self, method_output, raw_feedback)
|
||||
|
||||
# Distill: extract lessons from output + feedback, store in memory
|
||||
if learn and getattr(self, "memory", None) is not None and raw_feedback.strip():
|
||||
if (
|
||||
learn
|
||||
and getattr(self, "memory", None) is not None
|
||||
and raw_feedback.strip()
|
||||
):
|
||||
_distill_and_store_lessons(self, method_output, raw_feedback)
|
||||
|
||||
return result
|
||||
@@ -534,7 +546,7 @@ def human_feedback(
|
||||
metadata=metadata,
|
||||
provider=provider,
|
||||
learn=learn,
|
||||
learn_source=learn_source
|
||||
learn_source=learn_source,
|
||||
)
|
||||
wrapper.__is_flow_method__ = True
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
"""
|
||||
SQLite-based implementation of flow state persistence.
|
||||
"""
|
||||
"""SQLite-based implementation of flow state persistence."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sqlite3
|
||||
from typing import TYPE_CHECKING, Any
|
||||
@@ -13,6 +12,7 @@ from typing import TYPE_CHECKING, Any
|
||||
from pydantic import BaseModel
|
||||
|
||||
from crewai.flow.persistence.base import FlowPersistence
|
||||
from crewai.utilities.lock_store import lock as store_lock
|
||||
from crewai.utilities.paths import db_storage_path
|
||||
|
||||
|
||||
@@ -68,11 +68,15 @@ class SQLiteFlowPersistence(FlowPersistence):
|
||||
raise ValueError("Database path must be provided")
|
||||
|
||||
self.db_path = path # Now mypy knows this is str
|
||||
self._lock_name = f"sqlite:{os.path.realpath(self.db_path)}"
|
||||
self.init_db()
|
||||
|
||||
def init_db(self) -> None:
|
||||
"""Create the necessary tables if they don't exist."""
|
||||
with sqlite3.connect(self.db_path, timeout=30) as conn:
|
||||
with (
|
||||
store_lock(self._lock_name),
|
||||
sqlite3.connect(self.db_path, timeout=30) as conn,
|
||||
):
|
||||
conn.execute("PRAGMA journal_mode=WAL")
|
||||
# Main state table
|
||||
conn.execute(
|
||||
@@ -114,6 +118,49 @@ class SQLiteFlowPersistence(FlowPersistence):
|
||||
"""
|
||||
)
|
||||
|
||||
def _save_state_sql(
|
||||
self,
|
||||
conn: sqlite3.Connection,
|
||||
flow_uuid: str,
|
||||
method_name: str,
|
||||
state_dict: dict[str, Any],
|
||||
) -> None:
|
||||
"""Execute the save-state INSERT without acquiring the lock.
|
||||
|
||||
Args:
|
||||
conn: An open SQLite connection.
|
||||
flow_uuid: Unique identifier for the flow instance.
|
||||
method_name: Name of the method that just completed.
|
||||
state_dict: State data as a plain dict.
|
||||
"""
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO flow_states (
|
||||
flow_uuid,
|
||||
method_name,
|
||||
timestamp,
|
||||
state_json
|
||||
) VALUES (?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
flow_uuid,
|
||||
method_name,
|
||||
datetime.now(timezone.utc).isoformat(),
|
||||
json.dumps(state_dict),
|
||||
),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _to_state_dict(state_data: dict[str, Any] | BaseModel) -> dict[str, Any]:
|
||||
"""Convert state_data to a plain dict."""
|
||||
if isinstance(state_data, BaseModel):
|
||||
return state_data.model_dump()
|
||||
if isinstance(state_data, dict):
|
||||
return state_data
|
||||
raise ValueError(
|
||||
f"state_data must be either a Pydantic BaseModel or dict, got {type(state_data)}"
|
||||
)
|
||||
|
||||
def save_state(
|
||||
self,
|
||||
flow_uuid: str,
|
||||
@@ -127,33 +174,13 @@ class SQLiteFlowPersistence(FlowPersistence):
|
||||
method_name: Name of the method that just completed
|
||||
state_data: Current state data (either dict or Pydantic model)
|
||||
"""
|
||||
# Convert state_data to dict, handling both Pydantic and dict cases
|
||||
if isinstance(state_data, BaseModel):
|
||||
state_dict = state_data.model_dump()
|
||||
elif isinstance(state_data, dict):
|
||||
state_dict = state_data
|
||||
else:
|
||||
raise ValueError(
|
||||
f"state_data must be either a Pydantic BaseModel or dict, got {type(state_data)}"
|
||||
)
|
||||
state_dict = self._to_state_dict(state_data)
|
||||
|
||||
with sqlite3.connect(self.db_path, timeout=30) as conn:
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO flow_states (
|
||||
flow_uuid,
|
||||
method_name,
|
||||
timestamp,
|
||||
state_json
|
||||
) VALUES (?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
flow_uuid,
|
||||
method_name,
|
||||
datetime.now(timezone.utc).isoformat(),
|
||||
json.dumps(state_dict),
|
||||
),
|
||||
)
|
||||
with (
|
||||
store_lock(self._lock_name),
|
||||
sqlite3.connect(self.db_path, timeout=30) as conn,
|
||||
):
|
||||
self._save_state_sql(conn, flow_uuid, method_name, state_dict)
|
||||
|
||||
def load_state(self, flow_uuid: str) -> dict[str, Any] | None:
|
||||
"""Load the most recent state for a given flow UUID.
|
||||
@@ -198,24 +225,14 @@ class SQLiteFlowPersistence(FlowPersistence):
|
||||
context: The pending feedback context with all resume information
|
||||
state_data: Current state data
|
||||
"""
|
||||
# Import here to avoid circular imports
|
||||
state_dict = self._to_state_dict(state_data)
|
||||
|
||||
# Convert state_data to dict
|
||||
if isinstance(state_data, BaseModel):
|
||||
state_dict = state_data.model_dump()
|
||||
elif isinstance(state_data, dict):
|
||||
state_dict = state_data
|
||||
else:
|
||||
raise ValueError(
|
||||
f"state_data must be either a Pydantic BaseModel or dict, got {type(state_data)}"
|
||||
)
|
||||
with (
|
||||
store_lock(self._lock_name),
|
||||
sqlite3.connect(self.db_path, timeout=30) as conn,
|
||||
):
|
||||
self._save_state_sql(conn, flow_uuid, context.method_name, state_dict)
|
||||
|
||||
# Also save to regular state table for consistency
|
||||
self.save_state(flow_uuid, context.method_name, state_data)
|
||||
|
||||
# Save pending feedback context
|
||||
with sqlite3.connect(self.db_path, timeout=30) as conn:
|
||||
# Use INSERT OR REPLACE to handle re-triggering feedback on same flow
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT OR REPLACE INTO pending_feedback (
|
||||
@@ -273,7 +290,10 @@ class SQLiteFlowPersistence(FlowPersistence):
|
||||
Args:
|
||||
flow_uuid: Unique identifier for the flow instance
|
||||
"""
|
||||
with sqlite3.connect(self.db_path, timeout=30) as conn:
|
||||
with (
|
||||
store_lock(self._lock_name),
|
||||
sqlite3.connect(self.db_path, timeout=30) as conn,
|
||||
):
|
||||
conn.execute(
|
||||
"""
|
||||
DELETE FROM pending_feedback
|
||||
|
||||
@@ -11,6 +11,7 @@ into a standalone MCPToolResolver. It handles three flavours of MCP reference:
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import contextvars
|
||||
import time
|
||||
from typing import TYPE_CHECKING, Any, Final, cast
|
||||
from urllib.parse import urlparse
|
||||
@@ -22,10 +23,10 @@ from crewai.mcp.config import (
|
||||
MCPServerSSE,
|
||||
MCPServerStdio,
|
||||
)
|
||||
from crewai.utilities.string_utils import sanitize_tool_name
|
||||
from crewai.mcp.transports.http import HTTPTransport
|
||||
from crewai.mcp.transports.sse import SSETransport
|
||||
from crewai.mcp.transports.stdio import StdioTransport
|
||||
from crewai.utilities.string_utils import sanitize_tool_name
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -227,7 +228,9 @@ class MCPToolResolver:
|
||||
|
||||
server_params = {"url": server_url}
|
||||
server_name = self._extract_server_name(server_url)
|
||||
sanitized_specific_tool = sanitize_tool_name(specific_tool) if specific_tool else None
|
||||
sanitized_specific_tool = (
|
||||
sanitize_tool_name(specific_tool) if specific_tool else None
|
||||
)
|
||||
|
||||
try:
|
||||
tool_schemas = self._get_mcp_tool_schemas(server_params)
|
||||
@@ -353,9 +356,10 @@ class MCPToolResolver:
|
||||
asyncio.get_running_loop()
|
||||
import concurrent.futures
|
||||
|
||||
ctx = contextvars.copy_context()
|
||||
with concurrent.futures.ThreadPoolExecutor() as executor:
|
||||
future = executor.submit(
|
||||
asyncio.run, _setup_client_and_list_tools()
|
||||
ctx.run, asyncio.run, _setup_client_and_list_tools()
|
||||
)
|
||||
tools_list = future.result()
|
||||
except RuntimeError:
|
||||
|
||||
@@ -308,7 +308,9 @@ def analyze_for_save(
|
||||
return MemoryAnalysis.model_validate(response)
|
||||
except Exception as e:
|
||||
_logger.warning(
|
||||
"Memory save analysis failed, using defaults: %s", e, exc_info=False,
|
||||
"Memory save analysis failed, using defaults: %s",
|
||||
e,
|
||||
exc_info=False,
|
||||
)
|
||||
return _SAVE_DEFAULTS
|
||||
|
||||
@@ -366,6 +368,8 @@ def analyze_for_consolidation(
|
||||
return ConsolidationPlan.model_validate(response)
|
||||
except Exception as e:
|
||||
_logger.warning(
|
||||
"Consolidation analysis failed, defaulting to insert: %s", e, exc_info=False,
|
||||
"Consolidation analysis failed, defaulting to insert: %s",
|
||||
e,
|
||||
exc_info=False,
|
||||
)
|
||||
return _CONSOLIDATION_DEFAULT
|
||||
|
||||
@@ -11,6 +11,7 @@ Orchestrates the encoding side of memory in a single Flow with 5 steps:
|
||||
from __future__ import annotations
|
||||
|
||||
from concurrent.futures import Future, ThreadPoolExecutor
|
||||
import contextvars
|
||||
from datetime import datetime
|
||||
import math
|
||||
from typing import Any
|
||||
@@ -164,14 +165,20 @@ class EncodingFlow(Flow[EncodingState]):
|
||||
def parallel_find_similar(self) -> None:
|
||||
"""Search storage for similar records, concurrently for all active items."""
|
||||
items = list(self.state.items)
|
||||
active = [(i, item) for i, item in enumerate(items) if not item.dropped and item.embedding]
|
||||
active = [
|
||||
(i, item)
|
||||
for i, item in enumerate(items)
|
||||
if not item.dropped and item.embedding
|
||||
]
|
||||
|
||||
if not active:
|
||||
return
|
||||
|
||||
def _search_one(item: ItemState) -> list[tuple[MemoryRecord, float]]:
|
||||
def _search_one(
|
||||
item: ItemState,
|
||||
) -> list[tuple[MemoryRecord, float]]:
|
||||
scope_prefix = item.scope if item.scope and item.scope.strip("/") else None
|
||||
return self._storage.search(
|
||||
return self._storage.search( # type: ignore[no-any-return]
|
||||
item.embedding,
|
||||
scope_prefix=scope_prefix,
|
||||
categories=None,
|
||||
@@ -186,7 +193,14 @@ class EncodingFlow(Flow[EncodingState]):
|
||||
item.top_similarity = float(raw[0][1]) if raw else 0.0
|
||||
else:
|
||||
with ThreadPoolExecutor(max_workers=min(len(active), 8)) as pool:
|
||||
futures = [(i, item, pool.submit(_search_one, item)) for i, item in active]
|
||||
futures = [
|
||||
(
|
||||
i,
|
||||
item,
|
||||
pool.submit(contextvars.copy_context().run, _search_one, item),
|
||||
)
|
||||
for i, item in active
|
||||
]
|
||||
for _, item, future in futures:
|
||||
raw = future.result()
|
||||
item.similar_records = [r for r, _ in raw]
|
||||
@@ -250,24 +264,38 @@ class EncodingFlow(Flow[EncodingState]):
|
||||
# Group B: consolidation only
|
||||
self._apply_defaults(item)
|
||||
consol_futures[i] = pool.submit(
|
||||
contextvars.copy_context().run,
|
||||
analyze_for_consolidation,
|
||||
item.content, list(item.similar_records), self._llm,
|
||||
item.content,
|
||||
list(item.similar_records),
|
||||
self._llm,
|
||||
)
|
||||
elif not fields_provided and not has_similar:
|
||||
# Group C: field resolution only
|
||||
save_futures[i] = pool.submit(
|
||||
contextvars.copy_context().run,
|
||||
analyze_for_save,
|
||||
item.content, existing_scopes, existing_categories, self._llm,
|
||||
item.content,
|
||||
existing_scopes,
|
||||
existing_categories,
|
||||
self._llm,
|
||||
)
|
||||
else:
|
||||
# Group D: both in parallel
|
||||
save_futures[i] = pool.submit(
|
||||
contextvars.copy_context().run,
|
||||
analyze_for_save,
|
||||
item.content, existing_scopes, existing_categories, self._llm,
|
||||
item.content,
|
||||
existing_scopes,
|
||||
existing_categories,
|
||||
self._llm,
|
||||
)
|
||||
consol_futures[i] = pool.submit(
|
||||
contextvars.copy_context().run,
|
||||
analyze_for_consolidation,
|
||||
item.content, list(item.similar_records), self._llm,
|
||||
item.content,
|
||||
list(item.similar_records),
|
||||
self._llm,
|
||||
)
|
||||
|
||||
# Collect field-resolution results
|
||||
@@ -300,8 +328,8 @@ class EncodingFlow(Flow[EncodingState]):
|
||||
item.plan = ConsolidationPlan(actions=[], insert_new=True)
|
||||
|
||||
# Collect consolidation results
|
||||
for i, future in consol_futures.items():
|
||||
items[i].plan = future.result()
|
||||
for i, consol_future in consol_futures.items():
|
||||
items[i].plan = consol_future.result()
|
||||
finally:
|
||||
pool.shutdown(wait=False)
|
||||
|
||||
@@ -339,7 +367,9 @@ class EncodingFlow(Flow[EncodingState]):
|
||||
# similar_records overlap). Collect one action per record_id, first wins.
|
||||
# Also build a map from record_id to the original MemoryRecord for updates.
|
||||
dedup_deletes: set[str] = set() # record_ids to delete
|
||||
dedup_updates: dict[str, tuple[int, str]] = {} # record_id -> (item_idx, new_content)
|
||||
dedup_updates: dict[
|
||||
str, tuple[int, str]
|
||||
] = {} # record_id -> (item_idx, new_content)
|
||||
all_similar: dict[str, MemoryRecord] = {} # record_id -> MemoryRecord
|
||||
|
||||
for i, item in enumerate(items):
|
||||
@@ -350,13 +380,24 @@ class EncodingFlow(Flow[EncodingState]):
|
||||
all_similar[r.id] = r
|
||||
for action in item.plan.actions:
|
||||
rid = action.record_id
|
||||
if action.action == "delete" and rid not in dedup_deletes and rid not in dedup_updates:
|
||||
if (
|
||||
action.action == "delete"
|
||||
and rid not in dedup_deletes
|
||||
and rid not in dedup_updates
|
||||
):
|
||||
dedup_deletes.add(rid)
|
||||
elif action.action == "update" and action.new_content and rid not in dedup_deletes and rid not in dedup_updates:
|
||||
elif (
|
||||
action.action == "update"
|
||||
and action.new_content
|
||||
and rid not in dedup_deletes
|
||||
and rid not in dedup_updates
|
||||
):
|
||||
dedup_updates[rid] = (i, action.new_content)
|
||||
|
||||
# --- Batch re-embed all update contents in ONE call ---
|
||||
update_list = list(dedup_updates.items()) # [(record_id, (item_idx, new_content)), ...]
|
||||
update_list = list(
|
||||
dedup_updates.items()
|
||||
) # [(record_id, (item_idx, new_content)), ...]
|
||||
update_embeddings: list[list[float]] = []
|
||||
if update_list:
|
||||
update_contents = [content for _, (_, content) in update_list]
|
||||
@@ -377,51 +418,52 @@ class EncodingFlow(Flow[EncodingState]):
|
||||
if item.dropped or item.plan is None:
|
||||
continue
|
||||
if item.plan.insert_new:
|
||||
to_insert.append((i, MemoryRecord(
|
||||
content=item.content,
|
||||
scope=item.resolved_scope,
|
||||
categories=item.resolved_categories,
|
||||
metadata=item.resolved_metadata,
|
||||
importance=item.resolved_importance,
|
||||
embedding=item.embedding if item.embedding else None,
|
||||
source=item.resolved_source,
|
||||
private=item.resolved_private,
|
||||
)))
|
||||
|
||||
# All storage mutations under one lock so no other pipeline can
|
||||
# interleave and cause version conflicts. The lock is reentrant
|
||||
# (RLock) so the individual storage methods re-acquire it safely.
|
||||
updated_records: dict[str, MemoryRecord] = {}
|
||||
with self._storage.write_lock:
|
||||
if dedup_deletes:
|
||||
self._storage.delete(record_ids=list(dedup_deletes))
|
||||
self.state.records_deleted += len(dedup_deletes)
|
||||
|
||||
for rid, (_item_idx, new_content) in dedup_updates.items():
|
||||
existing = all_similar.get(rid)
|
||||
if existing is not None:
|
||||
new_emb = update_emb_map.get(rid, [])
|
||||
updated = MemoryRecord(
|
||||
id=existing.id,
|
||||
content=new_content,
|
||||
scope=existing.scope,
|
||||
categories=existing.categories,
|
||||
metadata=existing.metadata,
|
||||
importance=existing.importance,
|
||||
created_at=existing.created_at,
|
||||
last_accessed=now,
|
||||
embedding=new_emb if new_emb else existing.embedding,
|
||||
to_insert.append(
|
||||
(
|
||||
i,
|
||||
MemoryRecord(
|
||||
content=item.content,
|
||||
scope=item.resolved_scope,
|
||||
categories=item.resolved_categories,
|
||||
metadata=item.resolved_metadata,
|
||||
importance=item.resolved_importance,
|
||||
embedding=item.embedding if item.embedding else None,
|
||||
source=item.resolved_source,
|
||||
private=item.resolved_private,
|
||||
),
|
||||
)
|
||||
self._storage.update(updated)
|
||||
self.state.records_updated += 1
|
||||
updated_records[rid] = updated
|
||||
)
|
||||
|
||||
if to_insert:
|
||||
records = [r for _, r in to_insert]
|
||||
self._storage.save(records)
|
||||
self.state.records_inserted += len(records)
|
||||
for idx, record in to_insert:
|
||||
items[idx].result_record = record
|
||||
updated_records: dict[str, MemoryRecord] = {}
|
||||
if dedup_deletes:
|
||||
self._storage.delete(record_ids=list(dedup_deletes))
|
||||
self.state.records_deleted += len(dedup_deletes)
|
||||
|
||||
for rid, (_item_idx, new_content) in dedup_updates.items():
|
||||
existing = all_similar.get(rid)
|
||||
if existing is not None:
|
||||
new_emb = update_emb_map.get(rid, [])
|
||||
updated = MemoryRecord(
|
||||
id=existing.id,
|
||||
content=new_content,
|
||||
scope=existing.scope,
|
||||
categories=existing.categories,
|
||||
metadata=existing.metadata,
|
||||
importance=existing.importance,
|
||||
created_at=existing.created_at,
|
||||
last_accessed=now,
|
||||
embedding=new_emb if new_emb else existing.embedding,
|
||||
)
|
||||
self._storage.update(updated)
|
||||
self.state.records_updated += 1
|
||||
updated_records[rid] = updated
|
||||
|
||||
if to_insert:
|
||||
records = [r for _, r in to_insert]
|
||||
self._storage.save(records)
|
||||
self.state.records_inserted += len(records)
|
||||
for idx, record in to_insert:
|
||||
items[idx].result_record = record
|
||||
|
||||
# Set result_record for non-insert items (after lock, using updated_records)
|
||||
for _i, item in enumerate(items):
|
||||
|
||||
@@ -11,6 +11,7 @@ Implements adaptive-depth retrieval with:
|
||||
from __future__ import annotations
|
||||
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
import contextvars
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
from uuid import uuid4
|
||||
@@ -103,13 +104,12 @@ class RecallFlow(Flow[RecallState]):
|
||||
)
|
||||
# Post-filter by time cutoff
|
||||
if self.state.time_cutoff and raw:
|
||||
raw = [
|
||||
(r, s) for r, s in raw if r.created_at >= self.state.time_cutoff
|
||||
]
|
||||
raw = [(r, s) for r, s in raw if r.created_at >= self.state.time_cutoff]
|
||||
# Privacy filter
|
||||
if not self.state.include_private and raw:
|
||||
raw = [
|
||||
(r, s) for r, s in raw
|
||||
(r, s)
|
||||
for r, s in raw
|
||||
if not r.private or r.source == self.state.source
|
||||
]
|
||||
return scope, raw
|
||||
@@ -130,15 +130,20 @@ class RecallFlow(Flow[RecallState]):
|
||||
top_composite, _ = compute_composite_score(
|
||||
results[0][0], results[0][1], self._config
|
||||
)
|
||||
findings.append({
|
||||
"scope": scope,
|
||||
"results": results,
|
||||
"top_score": top_composite,
|
||||
})
|
||||
findings.append(
|
||||
{
|
||||
"scope": scope,
|
||||
"results": results,
|
||||
"top_score": top_composite,
|
||||
}
|
||||
)
|
||||
else:
|
||||
with ThreadPoolExecutor(max_workers=min(len(tasks), 4)) as pool:
|
||||
futures = {
|
||||
pool.submit(_search_one, emb, sc): (emb, sc)
|
||||
pool.submit(contextvars.copy_context().run, _search_one, emb, sc): (
|
||||
emb,
|
||||
sc,
|
||||
)
|
||||
for emb, sc in tasks
|
||||
}
|
||||
for future in as_completed(futures):
|
||||
@@ -147,16 +152,16 @@ class RecallFlow(Flow[RecallState]):
|
||||
top_composite, _ = compute_composite_score(
|
||||
results[0][0], results[0][1], self._config
|
||||
)
|
||||
findings.append({
|
||||
"scope": scope,
|
||||
"results": results,
|
||||
"top_score": top_composite,
|
||||
})
|
||||
findings.append(
|
||||
{
|
||||
"scope": scope,
|
||||
"results": results,
|
||||
"top_score": top_composite,
|
||||
}
|
||||
)
|
||||
|
||||
self.state.chunk_findings = findings
|
||||
self.state.confidence = max(
|
||||
(f["top_score"] for f in findings), default=0.0
|
||||
)
|
||||
self.state.confidence = max((f["top_score"] for f in findings), default=0.0)
|
||||
return findings
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
@@ -210,12 +215,16 @@ class RecallFlow(Flow[RecallState]):
|
||||
# Parse time_filter into a datetime cutoff
|
||||
if analysis.time_filter:
|
||||
try:
|
||||
self.state.time_cutoff = datetime.fromisoformat(analysis.time_filter)
|
||||
self.state.time_cutoff = datetime.fromisoformat(
|
||||
analysis.time_filter
|
||||
)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Batch-embed all sub-queries in ONE call
|
||||
queries = analysis.recall_queries if analysis.recall_queries else [self.state.query]
|
||||
queries = (
|
||||
analysis.recall_queries if analysis.recall_queries else [self.state.query]
|
||||
)
|
||||
queries = queries[:3]
|
||||
embeddings = embed_texts(self._embedder, queries)
|
||||
pairs: list[tuple[str, list[float]]] = [
|
||||
@@ -296,17 +305,21 @@ class RecallFlow(Flow[RecallState]):
|
||||
response = self._llm.call([{"role": "user", "content": prompt}])
|
||||
if isinstance(response, str) and "missing" in response.lower():
|
||||
self.state.evidence_gaps.append(response[:200])
|
||||
enhanced.append({
|
||||
"scope": finding["scope"],
|
||||
"extraction": response,
|
||||
"results": finding["results"],
|
||||
})
|
||||
enhanced.append(
|
||||
{
|
||||
"scope": finding["scope"],
|
||||
"extraction": response,
|
||||
"results": finding["results"],
|
||||
}
|
||||
)
|
||||
except Exception:
|
||||
enhanced.append({
|
||||
"scope": finding["scope"],
|
||||
"extraction": "",
|
||||
"results": finding["results"],
|
||||
})
|
||||
enhanced.append(
|
||||
{
|
||||
"scope": finding["scope"],
|
||||
"extraction": "",
|
||||
"results": finding["results"],
|
||||
}
|
||||
)
|
||||
self.state.chunk_findings = enhanced
|
||||
return enhanced
|
||||
|
||||
@@ -318,7 +331,7 @@ class RecallFlow(Flow[RecallState]):
|
||||
@router(re_search)
|
||||
def re_decide_depth(self) -> str:
|
||||
"""Re-evaluate depth after re-search. Same logic as decide_depth."""
|
||||
return self.decide_depth()
|
||||
return self.decide_depth() # type: ignore[call-arg]
|
||||
|
||||
@listen("synthesize")
|
||||
def synthesize_results(self) -> list[MemoryMatch]:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sqlite3
|
||||
from typing import Any
|
||||
@@ -8,6 +9,7 @@ from crewai.task import Task
|
||||
from crewai.utilities import Printer
|
||||
from crewai.utilities.crew_json_encoder import CrewJSONEncoder
|
||||
from crewai.utilities.errors import DatabaseError, DatabaseOperationError
|
||||
from crewai.utilities.lock_store import lock as store_lock
|
||||
from crewai.utilities.paths import db_storage_path
|
||||
|
||||
|
||||
@@ -24,6 +26,7 @@ class KickoffTaskOutputsSQLiteStorage:
|
||||
# Get the parent directory of the default db path and create our db file there
|
||||
db_path = str(Path(db_storage_path()) / "latest_kickoff_task_outputs.db")
|
||||
self.db_path = db_path
|
||||
self._lock_name = f"sqlite:{os.path.realpath(self.db_path)}"
|
||||
self._printer: Printer = Printer()
|
||||
self._initialize_db()
|
||||
|
||||
@@ -38,24 +41,25 @@ class KickoffTaskOutputsSQLiteStorage:
|
||||
DatabaseOperationError: If database initialization fails due to SQLite errors.
|
||||
"""
|
||||
try:
|
||||
with sqlite3.connect(self.db_path, timeout=30) as conn:
|
||||
conn.execute("PRAGMA journal_mode=WAL")
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
with store_lock(self._lock_name):
|
||||
with sqlite3.connect(self.db_path, timeout=30) as conn:
|
||||
conn.execute("PRAGMA journal_mode=WAL")
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS latest_kickoff_task_outputs (
|
||||
task_id TEXT PRIMARY KEY,
|
||||
expected_output TEXT,
|
||||
output JSON,
|
||||
task_index INTEGER,
|
||||
inputs JSON,
|
||||
was_replayed BOOLEAN,
|
||||
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS latest_kickoff_task_outputs (
|
||||
task_id TEXT PRIMARY KEY,
|
||||
expected_output TEXT,
|
||||
output JSON,
|
||||
task_index INTEGER,
|
||||
inputs JSON,
|
||||
was_replayed BOOLEAN,
|
||||
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
"""
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
conn.commit()
|
||||
except sqlite3.Error as e:
|
||||
error_msg = DatabaseError.format_error(DatabaseError.INIT_ERROR, e)
|
||||
logger.error(error_msg)
|
||||
@@ -83,25 +87,26 @@ class KickoffTaskOutputsSQLiteStorage:
|
||||
"""
|
||||
inputs = inputs or {}
|
||||
try:
|
||||
with sqlite3.connect(self.db_path, timeout=30) as conn:
|
||||
conn.execute("BEGIN TRANSACTION")
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
"""
|
||||
INSERT OR REPLACE INTO latest_kickoff_task_outputs
|
||||
(task_id, expected_output, output, task_index, inputs, was_replayed)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
str(task.id),
|
||||
task.expected_output,
|
||||
json.dumps(output, cls=CrewJSONEncoder),
|
||||
task_index,
|
||||
json.dumps(inputs, cls=CrewJSONEncoder),
|
||||
was_replayed,
|
||||
),
|
||||
)
|
||||
conn.commit()
|
||||
with store_lock(self._lock_name):
|
||||
with sqlite3.connect(self.db_path, timeout=30) as conn:
|
||||
conn.execute("BEGIN TRANSACTION")
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
"""
|
||||
INSERT OR REPLACE INTO latest_kickoff_task_outputs
|
||||
(task_id, expected_output, output, task_index, inputs, was_replayed)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
str(task.id),
|
||||
task.expected_output,
|
||||
json.dumps(output, cls=CrewJSONEncoder),
|
||||
task_index,
|
||||
json.dumps(inputs, cls=CrewJSONEncoder),
|
||||
was_replayed,
|
||||
),
|
||||
)
|
||||
conn.commit()
|
||||
except sqlite3.Error as e:
|
||||
error_msg = DatabaseError.format_error(DatabaseError.SAVE_ERROR, e)
|
||||
logger.error(error_msg)
|
||||
@@ -126,30 +131,31 @@ class KickoffTaskOutputsSQLiteStorage:
|
||||
DatabaseOperationError: If updating the task output fails due to SQLite errors.
|
||||
"""
|
||||
try:
|
||||
with sqlite3.connect(self.db_path, timeout=30) as conn:
|
||||
conn.execute("BEGIN TRANSACTION")
|
||||
cursor = conn.cursor()
|
||||
with store_lock(self._lock_name):
|
||||
with sqlite3.connect(self.db_path, timeout=30) as conn:
|
||||
conn.execute("BEGIN TRANSACTION")
|
||||
cursor = conn.cursor()
|
||||
|
||||
fields = []
|
||||
values = []
|
||||
for key, value in kwargs.items():
|
||||
fields.append(f"{key} = ?")
|
||||
values.append(
|
||||
json.dumps(value, cls=CrewJSONEncoder)
|
||||
if isinstance(value, dict)
|
||||
else value
|
||||
)
|
||||
fields = []
|
||||
values = []
|
||||
for key, value in kwargs.items():
|
||||
fields.append(f"{key} = ?")
|
||||
values.append(
|
||||
json.dumps(value, cls=CrewJSONEncoder)
|
||||
if isinstance(value, dict)
|
||||
else value
|
||||
)
|
||||
|
||||
query = f"UPDATE latest_kickoff_task_outputs SET {', '.join(fields)} WHERE task_index = ?" # nosec # noqa: S608
|
||||
values.append(task_index)
|
||||
query = f"UPDATE latest_kickoff_task_outputs SET {', '.join(fields)} WHERE task_index = ?" # nosec # noqa: S608
|
||||
values.append(task_index)
|
||||
|
||||
cursor.execute(query, tuple(values))
|
||||
conn.commit()
|
||||
cursor.execute(query, tuple(values))
|
||||
conn.commit()
|
||||
|
||||
if cursor.rowcount == 0:
|
||||
logger.warning(
|
||||
f"No row found with task_index {task_index}. No update performed."
|
||||
)
|
||||
if cursor.rowcount == 0:
|
||||
logger.warning(
|
||||
f"No row found with task_index {task_index}. No update performed."
|
||||
)
|
||||
except sqlite3.Error as e:
|
||||
error_msg = DatabaseError.format_error(DatabaseError.UPDATE_ERROR, e)
|
||||
logger.error(error_msg)
|
||||
@@ -206,11 +212,12 @@ class KickoffTaskOutputsSQLiteStorage:
|
||||
DatabaseOperationError: If deleting task outputs fails due to SQLite errors.
|
||||
"""
|
||||
try:
|
||||
with sqlite3.connect(self.db_path, timeout=30) as conn:
|
||||
conn.execute("BEGIN TRANSACTION")
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("DELETE FROM latest_kickoff_task_outputs")
|
||||
conn.commit()
|
||||
with store_lock(self._lock_name):
|
||||
with sqlite3.connect(self.db_path, timeout=30) as conn:
|
||||
conn.execute("BEGIN TRANSACTION")
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("DELETE FROM latest_kickoff_task_outputs")
|
||||
conn.commit()
|
||||
except sqlite3.Error as e:
|
||||
error_msg = DatabaseError.format_error(DatabaseError.DELETE_ERROR, e)
|
||||
logger.error(error_msg)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from contextlib import AbstractContextManager
|
||||
import contextvars
|
||||
from datetime import datetime
|
||||
import json
|
||||
import logging
|
||||
@@ -10,9 +10,9 @@ import os
|
||||
from pathlib import Path
|
||||
import threading
|
||||
import time
|
||||
from typing import Any, ClassVar
|
||||
from typing import Any
|
||||
|
||||
import lancedb
|
||||
import lancedb # type: ignore[import-untyped]
|
||||
|
||||
from crewai.memory.types import MemoryRecord, ScopeInfo
|
||||
from crewai.utilities.lock_store import lock as store_lock
|
||||
@@ -41,15 +41,6 @@ _RETRY_BASE_DELAY = 0.2 # seconds; doubles on each retry
|
||||
class LanceDBStorage:
|
||||
"""LanceDB-backed storage for the unified memory system."""
|
||||
|
||||
# Class-level registry: maps resolved database path -> shared write lock.
|
||||
# When multiple Memory instances (e.g. agent + crew) independently create
|
||||
# LanceDBStorage pointing at the same directory, they share one lock so
|
||||
# their writes don't conflict.
|
||||
# Uses RLock (reentrant) so callers can hold the lock for a batch of
|
||||
# operations while the individual methods re-acquire it without deadlocking.
|
||||
_path_locks: ClassVar[dict[str, threading.RLock]] = {}
|
||||
_path_locks_guard: ClassVar[threading.Lock] = threading.Lock()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
path: str | Path | None = None,
|
||||
@@ -85,44 +76,19 @@ class LanceDBStorage:
|
||||
self._table_name = table_name
|
||||
self._db = lancedb.connect(str(self._path))
|
||||
|
||||
# On macOS and Linux the default per-process open-file limit is 256.
|
||||
# A LanceDB table stores one file per fragment (one fragment per save()
|
||||
# call by default). With hundreds of fragments, a single full-table
|
||||
# scan opens all of them simultaneously, exhausting the limit.
|
||||
# Raise it proactively so scans on large tables never hit OS error 24.
|
||||
try:
|
||||
import resource
|
||||
|
||||
soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
|
||||
if soft < 4096:
|
||||
resource.setrlimit(resource.RLIMIT_NOFILE, (min(hard, 4096), hard))
|
||||
except Exception: # noqa: S110
|
||||
pass # Windows or already at the max hard limit — safe to ignore
|
||||
|
||||
self._compact_every = compact_every
|
||||
self._save_count = 0
|
||||
|
||||
self._lock_name = f"lancedb:{self._path.resolve()}"
|
||||
|
||||
resolved = str(self._path.resolve())
|
||||
with LanceDBStorage._path_locks_guard:
|
||||
if resolved not in LanceDBStorage._path_locks:
|
||||
LanceDBStorage._path_locks[resolved] = threading.RLock()
|
||||
self._write_lock = LanceDBStorage._path_locks[resolved]
|
||||
|
||||
# Try to open an existing table and infer dimension from its schema.
|
||||
# If no table exists yet, defer creation until the first save so the
|
||||
# dimension can be auto-detected from the embedder's actual output.
|
||||
try:
|
||||
self._table: lancedb.table.Table | None = self._db.open_table(
|
||||
self._table_name
|
||||
)
|
||||
self._table: Any = self._db.open_table(self._table_name)
|
||||
self._vector_dim: int = self._infer_dim_from_table(self._table)
|
||||
# Best-effort: create the scope index if it doesn't exist yet.
|
||||
with self._file_lock():
|
||||
with store_lock(self._lock_name):
|
||||
self._ensure_scope_index()
|
||||
# Compact in the background if the table has accumulated many
|
||||
# fragments from previous runs (each save() creates one).
|
||||
self._compact_if_needed()
|
||||
except Exception:
|
||||
self._table = None
|
||||
@@ -131,40 +97,25 @@ class LanceDBStorage:
|
||||
# Explicit dim provided: create the table immediately if it doesn't exist.
|
||||
if self._table is None and vector_dim is not None:
|
||||
self._vector_dim = vector_dim
|
||||
with self._file_lock():
|
||||
with store_lock(self._lock_name):
|
||||
self._table = self._create_table(vector_dim)
|
||||
|
||||
@property
|
||||
def write_lock(self) -> threading.RLock:
|
||||
"""The shared reentrant write lock for this database path.
|
||||
|
||||
Callers can acquire this to hold the lock across multiple storage
|
||||
operations (e.g. delete + update + save as one atomic batch).
|
||||
Individual methods also acquire it internally, but since it's
|
||||
reentrant (RLock), the same thread won't deadlock.
|
||||
"""
|
||||
return self._write_lock
|
||||
|
||||
@staticmethod
|
||||
def _infer_dim_from_table(table: lancedb.table.Table) -> int:
|
||||
def _infer_dim_from_table(table: Any) -> int:
|
||||
"""Read vector dimension from an existing table's schema."""
|
||||
schema = table.schema
|
||||
for field in schema:
|
||||
if field.name == "vector":
|
||||
try:
|
||||
return field.type.list_size
|
||||
return int(field.type.list_size)
|
||||
except Exception:
|
||||
break
|
||||
return DEFAULT_VECTOR_DIM
|
||||
|
||||
def _file_lock(self) -> AbstractContextManager[None]:
|
||||
"""Return a cross-process lock for serialising writes."""
|
||||
return store_lock(self._lock_name)
|
||||
|
||||
def _do_write(self, op: str, *args: Any, **kwargs: Any) -> Any:
|
||||
"""Execute a single table write with retry on commit conflicts.
|
||||
|
||||
Caller must already hold the cross-process file lock.
|
||||
Caller must already hold ``store_lock(self._lock_name)``.
|
||||
"""
|
||||
delay = _RETRY_BASE_DELAY
|
||||
for attempt in range(_MAX_RETRIES + 1):
|
||||
@@ -188,10 +139,10 @@ class LanceDBStorage:
|
||||
delay *= 2
|
||||
return None # unreachable, but satisfies type checker
|
||||
|
||||
def _create_table(self, vector_dim: int) -> lancedb.table.Table:
|
||||
def _create_table(self, vector_dim: int) -> Any:
|
||||
"""Create a new table with the given vector dimension.
|
||||
|
||||
Caller must already hold the cross-process file lock.
|
||||
Caller must already hold ``store_lock(self._lock_name)``.
|
||||
"""
|
||||
placeholder = [
|
||||
{
|
||||
@@ -250,8 +201,10 @@ class LanceDBStorage:
|
||||
|
||||
def _compact_async(self) -> None:
|
||||
"""Fire-and-forget: compact the table in a daemon background thread."""
|
||||
ctx = contextvars.copy_context()
|
||||
threading.Thread(
|
||||
target=self._compact_safe,
|
||||
target=ctx.run,
|
||||
args=(self._compact_safe,),
|
||||
daemon=True,
|
||||
name="lancedb-compact",
|
||||
).start()
|
||||
@@ -260,13 +213,13 @@ class LanceDBStorage:
|
||||
"""Run ``table.optimize()`` in a background thread, absorbing errors."""
|
||||
try:
|
||||
if self._table is not None:
|
||||
with self._file_lock():
|
||||
with store_lock(self._lock_name):
|
||||
self._table.optimize()
|
||||
self._ensure_scope_index()
|
||||
except Exception:
|
||||
_logger.debug("LanceDB background compaction failed", exc_info=True)
|
||||
|
||||
def _ensure_table(self, vector_dim: int | None = None) -> lancedb.table.Table:
|
||||
def _ensure_table(self, vector_dim: int | None = None) -> Any:
|
||||
"""Return the table, creating it lazily if needed.
|
||||
|
||||
Args:
|
||||
@@ -332,12 +285,12 @@ class LanceDBStorage:
|
||||
dim = len(r.embedding)
|
||||
break
|
||||
is_new_table = self._table is None
|
||||
with self._write_lock, self._file_lock():
|
||||
with store_lock(self._lock_name):
|
||||
self._ensure_table(vector_dim=dim)
|
||||
rows = [self._record_to_row(r) for r in records]
|
||||
for r in rows:
|
||||
if r["vector"] is None or len(r["vector"]) != self._vector_dim:
|
||||
r["vector"] = [0.0] * self._vector_dim
|
||||
rows = [self._record_to_row(rec) for rec in records]
|
||||
for row in rows:
|
||||
if row["vector"] is None or len(row["vector"]) != self._vector_dim:
|
||||
row["vector"] = [0.0] * self._vector_dim
|
||||
self._do_write("add", rows)
|
||||
if is_new_table:
|
||||
self._ensure_scope_index()
|
||||
@@ -348,7 +301,7 @@ class LanceDBStorage:
|
||||
|
||||
def update(self, record: MemoryRecord) -> None:
|
||||
"""Update a record by ID. Preserves created_at, updates last_accessed."""
|
||||
with self._write_lock, self._file_lock():
|
||||
with store_lock(self._lock_name):
|
||||
self._ensure_table()
|
||||
safe_id = str(record.id).replace("'", "''")
|
||||
self._do_write("delete", f"id = '{safe_id}'")
|
||||
@@ -369,7 +322,7 @@ class LanceDBStorage:
|
||||
"""
|
||||
if not record_ids or self._table is None:
|
||||
return
|
||||
with self._write_lock, self._file_lock():
|
||||
with store_lock(self._lock_name):
|
||||
now = datetime.utcnow().isoformat()
|
||||
safe_ids = [str(rid).replace("'", "''") for rid in record_ids]
|
||||
ids_expr = ", ".join(f"'{rid}'" for rid in safe_ids)
|
||||
@@ -383,11 +336,12 @@ class LanceDBStorage:
|
||||
"""Return a single record by ID, or None if not found."""
|
||||
if self._table is None:
|
||||
return None
|
||||
safe_id = str(record_id).replace("'", "''")
|
||||
rows = self._table.search().where(f"id = '{safe_id}'").limit(1).to_list()
|
||||
if not rows:
|
||||
return None
|
||||
return self._row_to_record(rows[0])
|
||||
with store_lock(self._lock_name):
|
||||
safe_id = str(record_id).replace("'", "''")
|
||||
rows = self._table.search().where(f"id = '{safe_id}'").limit(1).to_list()
|
||||
if not rows:
|
||||
return None
|
||||
return self._row_to_record(rows[0])
|
||||
|
||||
def search(
|
||||
self,
|
||||
@@ -400,14 +354,15 @@ class LanceDBStorage:
|
||||
) -> list[tuple[MemoryRecord, float]]:
|
||||
if self._table is None:
|
||||
return []
|
||||
query = self._table.search(query_embedding)
|
||||
if scope_prefix is not None and scope_prefix.strip("/"):
|
||||
prefix = scope_prefix.rstrip("/")
|
||||
like_val = prefix + "%"
|
||||
query = query.where(f"scope LIKE '{like_val}'")
|
||||
results = query.limit(
|
||||
limit * 3 if (categories or metadata_filter) else limit
|
||||
).to_list()
|
||||
with store_lock(self._lock_name):
|
||||
query = self._table.search(query_embedding)
|
||||
if scope_prefix is not None and scope_prefix.strip("/"):
|
||||
prefix = scope_prefix.rstrip("/")
|
||||
like_val = prefix + "%"
|
||||
query = query.where(f"scope LIKE '{like_val}'")
|
||||
results = query.limit(
|
||||
limit * 3 if (categories or metadata_filter) else limit
|
||||
).to_list()
|
||||
out: list[tuple[MemoryRecord, float]] = []
|
||||
for row in results:
|
||||
record = self._row_to_record(row)
|
||||
@@ -435,12 +390,12 @@ class LanceDBStorage:
|
||||
) -> int:
|
||||
if self._table is None:
|
||||
return 0
|
||||
with self._write_lock, self._file_lock():
|
||||
with store_lock(self._lock_name):
|
||||
if record_ids and not (categories or metadata_filter):
|
||||
before = self._table.count_rows()
|
||||
before = int(self._table.count_rows())
|
||||
ids_expr = ", ".join(f"'{rid}'" for rid in record_ids)
|
||||
self._do_write("delete", f"id IN ({ids_expr})")
|
||||
return before - self._table.count_rows()
|
||||
return before - int(self._table.count_rows())
|
||||
if categories or metadata_filter:
|
||||
rows = self._scan_rows(scope_prefix)
|
||||
to_delete: list[str] = []
|
||||
@@ -459,10 +414,10 @@ class LanceDBStorage:
|
||||
to_delete.append(record.id)
|
||||
if not to_delete:
|
||||
return 0
|
||||
before = self._table.count_rows()
|
||||
before = int(self._table.count_rows())
|
||||
ids_expr = ", ".join(f"'{rid}'" for rid in to_delete)
|
||||
self._do_write("delete", f"id IN ({ids_expr})")
|
||||
return before - self._table.count_rows()
|
||||
return before - int(self._table.count_rows())
|
||||
conditions = []
|
||||
if scope_prefix is not None and scope_prefix.strip("/"):
|
||||
prefix = scope_prefix.rstrip("/")
|
||||
@@ -472,13 +427,13 @@ class LanceDBStorage:
|
||||
if older_than is not None:
|
||||
conditions.append(f"created_at < '{older_than.isoformat()}'")
|
||||
if not conditions:
|
||||
before = self._table.count_rows()
|
||||
before = int(self._table.count_rows())
|
||||
self._do_write("delete", "id != ''")
|
||||
return before - self._table.count_rows()
|
||||
return before - int(self._table.count_rows())
|
||||
where_expr = " AND ".join(conditions)
|
||||
before = self._table.count_rows()
|
||||
before = int(self._table.count_rows())
|
||||
self._do_write("delete", where_expr)
|
||||
return before - self._table.count_rows()
|
||||
return before - int(self._table.count_rows())
|
||||
|
||||
def _scan_rows(
|
||||
self,
|
||||
@@ -491,6 +446,8 @@ class LanceDBStorage:
|
||||
Uses a full table scan (no vector query) so the limit is applied after
|
||||
the scope filter, not to ANN candidates before filtering.
|
||||
|
||||
Caller must hold ``store_lock(self._lock_name)``.
|
||||
|
||||
Args:
|
||||
scope_prefix: Optional scope path prefix to filter by.
|
||||
limit: Maximum number of rows to return (applied after filtering).
|
||||
@@ -505,7 +462,8 @@ class LanceDBStorage:
|
||||
q = q.where(f"scope LIKE '{scope_prefix.rstrip('/')}%'")
|
||||
if columns is not None:
|
||||
q = q.select(columns)
|
||||
return q.limit(limit).to_list()
|
||||
result: list[dict[str, Any]] = q.limit(limit).to_list()
|
||||
return result
|
||||
|
||||
def list_records(
|
||||
self, scope_prefix: str | None = None, limit: int = 200, offset: int = 0
|
||||
@@ -520,7 +478,8 @@ class LanceDBStorage:
|
||||
Returns:
|
||||
List of MemoryRecord, ordered by created_at descending.
|
||||
"""
|
||||
rows = self._scan_rows(scope_prefix, limit=limit + offset)
|
||||
with store_lock(self._lock_name):
|
||||
rows = self._scan_rows(scope_prefix, limit=limit + offset)
|
||||
records = [self._row_to_record(r) for r in rows]
|
||||
records.sort(key=lambda r: r.created_at, reverse=True)
|
||||
return records[offset : offset + limit]
|
||||
@@ -530,10 +489,11 @@ class LanceDBStorage:
|
||||
prefix = scope if scope != "/" else ""
|
||||
if prefix and not prefix.startswith("/"):
|
||||
prefix = "/" + prefix
|
||||
rows = self._scan_rows(
|
||||
prefix or None,
|
||||
columns=["scope", "categories_str", "created_at"],
|
||||
)
|
||||
with store_lock(self._lock_name):
|
||||
rows = self._scan_rows(
|
||||
prefix or None,
|
||||
columns=["scope", "categories_str", "created_at"],
|
||||
)
|
||||
if not rows:
|
||||
return ScopeInfo(
|
||||
path=scope or "/",
|
||||
@@ -584,7 +544,8 @@ class LanceDBStorage:
|
||||
def list_scopes(self, parent: str = "/") -> list[str]:
|
||||
parent = parent.rstrip("/") or ""
|
||||
prefix = (parent + "/") if parent else "/"
|
||||
rows = self._scan_rows(prefix if prefix != "/" else None, columns=["scope"])
|
||||
with store_lock(self._lock_name):
|
||||
rows = self._scan_rows(prefix if prefix != "/" else None, columns=["scope"])
|
||||
children: set[str] = set()
|
||||
for row in rows:
|
||||
sc = str(row.get("scope", ""))
|
||||
@@ -596,7 +557,8 @@ class LanceDBStorage:
|
||||
return sorted(children)
|
||||
|
||||
def list_categories(self, scope_prefix: str | None = None) -> dict[str, int]:
|
||||
rows = self._scan_rows(scope_prefix, columns=["categories_str"])
|
||||
with store_lock(self._lock_name):
|
||||
rows = self._scan_rows(scope_prefix, columns=["categories_str"])
|
||||
counts: dict[str, int] = {}
|
||||
for row in rows:
|
||||
cat_str = row.get("categories_str") or "[]"
|
||||
@@ -612,12 +574,13 @@ class LanceDBStorage:
|
||||
if self._table is None:
|
||||
return 0
|
||||
if scope_prefix is None or scope_prefix.strip("/") == "":
|
||||
return self._table.count_rows()
|
||||
with store_lock(self._lock_name):
|
||||
return int(self._table.count_rows())
|
||||
info = self.get_scope_info(scope_prefix)
|
||||
return info.record_count
|
||||
|
||||
def reset(self, scope_prefix: str | None = None) -> None:
|
||||
with self._write_lock, self._file_lock():
|
||||
with store_lock(self._lock_name):
|
||||
if scope_prefix is None or scope_prefix.strip("/") == "":
|
||||
if self._table is not None:
|
||||
self._db.drop_table(self._table_name)
|
||||
@@ -643,7 +606,7 @@ class LanceDBStorage:
|
||||
"""
|
||||
if self._table is None:
|
||||
return
|
||||
with self._write_lock, self._file_lock():
|
||||
with store_lock(self._lock_name):
|
||||
self._table.optimize()
|
||||
self._ensure_scope_index()
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from concurrent.futures import Future, ThreadPoolExecutor
|
||||
import contextvars
|
||||
from datetime import datetime
|
||||
import threading
|
||||
import time
|
||||
@@ -229,8 +230,9 @@ class Memory(BaseModel):
|
||||
If the pool has been shut down (e.g. after ``close()``), the save
|
||||
runs synchronously as a fallback so late saves still succeed.
|
||||
"""
|
||||
ctx = contextvars.copy_context()
|
||||
try:
|
||||
future: Future[Any] = self._save_pool.submit(fn, *args, **kwargs)
|
||||
future: Future[Any] = self._save_pool.submit(ctx.run, fn, *args, **kwargs)
|
||||
except RuntimeError:
|
||||
# Pool shut down -- run synchronously as fallback
|
||||
future = Future()
|
||||
|
||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Callable
|
||||
import contextvars
|
||||
from functools import wraps
|
||||
import inspect
|
||||
from typing import TYPE_CHECKING, Any, Concatenate, ParamSpec, TypeVar, overload
|
||||
@@ -169,8 +170,9 @@ def _call_method(method: Callable[..., Any], *args: Any, **kwargs: Any) -> Any:
|
||||
if loop and loop.is_running():
|
||||
import concurrent.futures
|
||||
|
||||
ctx = contextvars.copy_context()
|
||||
with concurrent.futures.ThreadPoolExecutor() as pool:
|
||||
return pool.submit(asyncio.run, result).result()
|
||||
return pool.submit(ctx.run, asyncio.run, result).result()
|
||||
return asyncio.run(result)
|
||||
return result
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Callable
|
||||
import contextvars
|
||||
from functools import partial
|
||||
import inspect
|
||||
from pathlib import Path
|
||||
@@ -146,8 +147,9 @@ def _resolve_result(result: Any) -> Any:
|
||||
if loop and loop.is_running():
|
||||
import concurrent.futures
|
||||
|
||||
ctx = contextvars.copy_context()
|
||||
with concurrent.futures.ThreadPoolExecutor() as pool:
|
||||
return pool.submit(asyncio.run, result).result()
|
||||
return pool.submit(ctx.run, asyncio.run, result).result()
|
||||
return asyncio.run(result)
|
||||
return result
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
"""ChromaDB client implementation."""
|
||||
|
||||
import asyncio
|
||||
from collections.abc import AsyncIterator
|
||||
from contextlib import AbstractContextManager, asynccontextmanager, nullcontext
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
@@ -29,6 +32,7 @@ from crewai.rag.core.base_client import (
|
||||
BaseCollectionParams,
|
||||
)
|
||||
from crewai.rag.types import SearchResult
|
||||
from crewai.utilities.lock_store import lock as store_lock
|
||||
from crewai.utilities.logger_utils import suppress_logging
|
||||
|
||||
|
||||
@@ -52,6 +56,7 @@ class ChromaDBClient(BaseClient):
|
||||
default_limit: int = 5,
|
||||
default_score_threshold: float = 0.6,
|
||||
default_batch_size: int = 100,
|
||||
lock_name: str = "",
|
||||
) -> None:
|
||||
"""Initialize ChromaDBClient with client and embedding function.
|
||||
|
||||
@@ -61,12 +66,32 @@ class ChromaDBClient(BaseClient):
|
||||
default_limit: Default number of results to return in searches.
|
||||
default_score_threshold: Default minimum score for search results.
|
||||
default_batch_size: Default batch size for adding documents.
|
||||
lock_name: Optional lock name for cross-process synchronization.
|
||||
"""
|
||||
self.client = client
|
||||
self.embedding_function = embedding_function
|
||||
self.default_limit = default_limit
|
||||
self.default_score_threshold = default_score_threshold
|
||||
self.default_batch_size = default_batch_size
|
||||
self._lock_name = lock_name
|
||||
|
||||
def _locked(self) -> AbstractContextManager[None]:
|
||||
"""Return a cross-process lock context manager, or nullcontext if no lock name."""
|
||||
return store_lock(self._lock_name) if self._lock_name else nullcontext()
|
||||
|
||||
@asynccontextmanager
|
||||
async def _alocked(self) -> AsyncIterator[None]:
|
||||
"""Async cross-process lock that acquires/releases in an executor."""
|
||||
if not self._lock_name:
|
||||
yield
|
||||
return
|
||||
lock_cm = store_lock(self._lock_name)
|
||||
loop = asyncio.get_event_loop()
|
||||
await loop.run_in_executor(None, lock_cm.__enter__)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
await loop.run_in_executor(None, lock_cm.__exit__, None, None, None)
|
||||
|
||||
def create_collection(
|
||||
self, **kwargs: Unpack[ChromaDBCollectionCreateParams]
|
||||
@@ -313,23 +338,24 @@ class ChromaDBClient(BaseClient):
|
||||
if not documents:
|
||||
raise ValueError("Documents list cannot be empty")
|
||||
|
||||
collection = self.client.get_or_create_collection(
|
||||
name=_sanitize_collection_name(collection_name),
|
||||
embedding_function=self.embedding_function,
|
||||
)
|
||||
|
||||
prepared = _prepare_documents_for_chromadb(documents)
|
||||
|
||||
for i in range(0, len(prepared.ids), batch_size):
|
||||
batch_ids, batch_texts, batch_metadatas = _create_batch_slice(
|
||||
prepared=prepared, start_index=i, batch_size=batch_size
|
||||
with self._locked():
|
||||
collection = self.client.get_or_create_collection(
|
||||
name=_sanitize_collection_name(collection_name),
|
||||
embedding_function=self.embedding_function,
|
||||
)
|
||||
|
||||
collection.upsert(
|
||||
ids=batch_ids,
|
||||
documents=batch_texts,
|
||||
metadatas=batch_metadatas, # type: ignore[arg-type]
|
||||
)
|
||||
prepared = _prepare_documents_for_chromadb(documents)
|
||||
|
||||
for i in range(0, len(prepared.ids), batch_size):
|
||||
batch_ids, batch_texts, batch_metadatas = _create_batch_slice(
|
||||
prepared=prepared, start_index=i, batch_size=batch_size
|
||||
)
|
||||
|
||||
collection.upsert(
|
||||
ids=batch_ids,
|
||||
documents=batch_texts,
|
||||
metadatas=batch_metadatas, # type: ignore[arg-type]
|
||||
)
|
||||
|
||||
async def aadd_documents(self, **kwargs: Unpack[BaseCollectionAddParams]) -> None:
|
||||
"""Add documents with their embeddings to a collection asynchronously.
|
||||
@@ -363,22 +389,23 @@ class ChromaDBClient(BaseClient):
|
||||
if not documents:
|
||||
raise ValueError("Documents list cannot be empty")
|
||||
|
||||
collection = await self.client.get_or_create_collection(
|
||||
name=_sanitize_collection_name(collection_name),
|
||||
embedding_function=self.embedding_function,
|
||||
)
|
||||
prepared = _prepare_documents_for_chromadb(documents)
|
||||
|
||||
for i in range(0, len(prepared.ids), batch_size):
|
||||
batch_ids, batch_texts, batch_metadatas = _create_batch_slice(
|
||||
prepared=prepared, start_index=i, batch_size=batch_size
|
||||
async with self._alocked():
|
||||
collection = await self.client.get_or_create_collection(
|
||||
name=_sanitize_collection_name(collection_name),
|
||||
embedding_function=self.embedding_function,
|
||||
)
|
||||
prepared = _prepare_documents_for_chromadb(documents)
|
||||
|
||||
await collection.upsert(
|
||||
ids=batch_ids,
|
||||
documents=batch_texts,
|
||||
metadatas=batch_metadatas, # type: ignore[arg-type]
|
||||
)
|
||||
for i in range(0, len(prepared.ids), batch_size):
|
||||
batch_ids, batch_texts, batch_metadatas = _create_batch_slice(
|
||||
prepared=prepared, start_index=i, batch_size=batch_size
|
||||
)
|
||||
|
||||
await collection.upsert(
|
||||
ids=batch_ids,
|
||||
documents=batch_texts,
|
||||
metadatas=batch_metadatas, # type: ignore[arg-type]
|
||||
)
|
||||
|
||||
def search(
|
||||
self, **kwargs: Unpack[ChromaDBCollectionSearchParams]
|
||||
@@ -419,29 +446,30 @@ class ChromaDBClient(BaseClient):
|
||||
|
||||
params = _extract_search_params(kwargs)
|
||||
|
||||
collection = self.client.get_or_create_collection(
|
||||
name=_sanitize_collection_name(params.collection_name),
|
||||
embedding_function=self.embedding_function,
|
||||
)
|
||||
|
||||
where = params.where if params.where is not None else params.metadata_filter
|
||||
|
||||
with suppress_logging(
|
||||
"chromadb.segment.impl.vector.local_persistent_hnsw", logging.ERROR
|
||||
):
|
||||
results: QueryResult = collection.query(
|
||||
query_texts=[params.query],
|
||||
n_results=params.limit,
|
||||
where=where,
|
||||
where_document=params.where_document,
|
||||
include=params.include,
|
||||
with self._locked():
|
||||
collection = self.client.get_or_create_collection(
|
||||
name=_sanitize_collection_name(params.collection_name),
|
||||
embedding_function=self.embedding_function,
|
||||
)
|
||||
|
||||
return _process_query_results(
|
||||
collection=collection,
|
||||
results=results,
|
||||
params=params,
|
||||
)
|
||||
where = params.where if params.where is not None else params.metadata_filter
|
||||
|
||||
with suppress_logging(
|
||||
"chromadb.segment.impl.vector.local_persistent_hnsw", logging.ERROR
|
||||
):
|
||||
results: QueryResult = collection.query(
|
||||
query_texts=[params.query],
|
||||
n_results=params.limit,
|
||||
where=where,
|
||||
where_document=params.where_document,
|
||||
include=params.include,
|
||||
)
|
||||
|
||||
return _process_query_results(
|
||||
collection=collection,
|
||||
results=results,
|
||||
params=params,
|
||||
)
|
||||
|
||||
async def asearch(
|
||||
self, **kwargs: Unpack[ChromaDBCollectionSearchParams]
|
||||
@@ -482,29 +510,30 @@ class ChromaDBClient(BaseClient):
|
||||
|
||||
params = _extract_search_params(kwargs)
|
||||
|
||||
collection = await self.client.get_or_create_collection(
|
||||
name=_sanitize_collection_name(params.collection_name),
|
||||
embedding_function=self.embedding_function,
|
||||
)
|
||||
|
||||
where = params.where if params.where is not None else params.metadata_filter
|
||||
|
||||
with suppress_logging(
|
||||
"chromadb.segment.impl.vector.local_persistent_hnsw", logging.ERROR
|
||||
):
|
||||
results: QueryResult = await collection.query(
|
||||
query_texts=[params.query],
|
||||
n_results=params.limit,
|
||||
where=where,
|
||||
where_document=params.where_document,
|
||||
include=params.include,
|
||||
async with self._alocked():
|
||||
collection = await self.client.get_or_create_collection(
|
||||
name=_sanitize_collection_name(params.collection_name),
|
||||
embedding_function=self.embedding_function,
|
||||
)
|
||||
|
||||
return _process_query_results(
|
||||
collection=collection,
|
||||
results=results,
|
||||
params=params,
|
||||
)
|
||||
where = params.where if params.where is not None else params.metadata_filter
|
||||
|
||||
with suppress_logging(
|
||||
"chromadb.segment.impl.vector.local_persistent_hnsw", logging.ERROR
|
||||
):
|
||||
results: QueryResult = await collection.query(
|
||||
query_texts=[params.query],
|
||||
n_results=params.limit,
|
||||
where=where,
|
||||
where_document=params.where_document,
|
||||
include=params.include,
|
||||
)
|
||||
|
||||
return _process_query_results(
|
||||
collection=collection,
|
||||
results=results,
|
||||
params=params,
|
||||
)
|
||||
|
||||
def delete_collection(self, **kwargs: Unpack[BaseCollectionParams]) -> None:
|
||||
"""Delete a collection and all its data.
|
||||
@@ -531,7 +560,10 @@ class ChromaDBClient(BaseClient):
|
||||
)
|
||||
|
||||
collection_name = kwargs["collection_name"]
|
||||
self.client.delete_collection(name=_sanitize_collection_name(collection_name))
|
||||
with self._locked():
|
||||
self.client.delete_collection(
|
||||
name=_sanitize_collection_name(collection_name)
|
||||
)
|
||||
|
||||
async def adelete_collection(self, **kwargs: Unpack[BaseCollectionParams]) -> None:
|
||||
"""Delete a collection and all its data asynchronously.
|
||||
@@ -561,9 +593,10 @@ class ChromaDBClient(BaseClient):
|
||||
)
|
||||
|
||||
collection_name = kwargs["collection_name"]
|
||||
await self.client.delete_collection(
|
||||
name=_sanitize_collection_name(collection_name)
|
||||
)
|
||||
async with self._alocked():
|
||||
await self.client.delete_collection(
|
||||
name=_sanitize_collection_name(collection_name)
|
||||
)
|
||||
|
||||
def reset(self) -> None:
|
||||
"""Reset the vector database by deleting all collections and data.
|
||||
@@ -586,7 +619,8 @@ class ChromaDBClient(BaseClient):
|
||||
"Use areset() for AsyncClientAPI."
|
||||
)
|
||||
|
||||
self.client.reset()
|
||||
with self._locked():
|
||||
self.client.reset()
|
||||
|
||||
async def areset(self) -> None:
|
||||
"""Reset the vector database by deleting all collections and data asynchronously.
|
||||
@@ -612,4 +646,5 @@ class ChromaDBClient(BaseClient):
|
||||
"Use reset() for ClientAPI."
|
||||
)
|
||||
|
||||
await self.client.reset()
|
||||
async with self._alocked():
|
||||
await self.client.reset()
|
||||
|
||||
@@ -39,4 +39,5 @@ def create_client(config: ChromaDBConfig) -> ChromaDBClient:
|
||||
default_limit=config.limit,
|
||||
default_score_threshold=config.score_threshold,
|
||||
default_batch_size=config.batch_size,
|
||||
lock_name=f"chromadb:{persist_dir}",
|
||||
)
|
||||
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from concurrent.futures import Future
|
||||
import contextvars
|
||||
from copy import copy as shallow_copy
|
||||
import datetime
|
||||
from hashlib import md5
|
||||
@@ -524,10 +525,11 @@ class Task(BaseModel):
|
||||
) -> Future[TaskOutput]:
|
||||
"""Execute the task asynchronously."""
|
||||
future: Future[TaskOutput] = Future()
|
||||
ctx = contextvars.copy_context()
|
||||
threading.Thread(
|
||||
daemon=True,
|
||||
target=self._execute_task_async,
|
||||
args=(agent, context, tools, future),
|
||||
target=ctx.run,
|
||||
args=(self._execute_task_async, agent, context, tools, future),
|
||||
).start()
|
||||
return future
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ concurrently by the executor.
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Callable
|
||||
import contextvars
|
||||
from typing import Any
|
||||
|
||||
from crewai.tools import BaseTool
|
||||
@@ -84,9 +85,10 @@ class MCPNativeTool(BaseTool):
|
||||
|
||||
import concurrent.futures
|
||||
|
||||
ctx = contextvars.copy_context()
|
||||
with concurrent.futures.ThreadPoolExecutor() as executor:
|
||||
coro = self._run_async(**kwargs)
|
||||
future = executor.submit(asyncio.run, coro)
|
||||
future = executor.submit(ctx.run, asyncio.run, coro)
|
||||
return future.result()
|
||||
except RuntimeError:
|
||||
return asyncio.run(self._run_async(**kwargs))
|
||||
|
||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
from collections.abc import Callable, Sequence
|
||||
import concurrent.futures
|
||||
import contextvars
|
||||
import inspect
|
||||
import json
|
||||
import re
|
||||
@@ -907,8 +908,9 @@ def summarize_messages(
|
||||
chunks=chunks, llm=llm, callbacks=callbacks, i18n=i18n
|
||||
)
|
||||
if is_inside_event_loop():
|
||||
ctx = contextvars.copy_context()
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
|
||||
summarized_contents = pool.submit(asyncio.run, coro).result()
|
||||
summarized_contents = pool.submit(ctx.run, asyncio.run, coro).result()
|
||||
else:
|
||||
summarized_contents = asyncio.run(coro)
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ from typing import Any, TypedDict
|
||||
|
||||
from typing_extensions import Unpack
|
||||
|
||||
from crewai.utilities.lock_store import lock as store_lock
|
||||
|
||||
|
||||
class LogEntry(TypedDict, total=False):
|
||||
"""TypedDict for log entry kwargs with optional fields for flexibility."""
|
||||
@@ -90,33 +92,36 @@ class FileHandler:
|
||||
ValueError: If logging fails.
|
||||
"""
|
||||
try:
|
||||
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
log_entry = {"timestamp": now, **kwargs}
|
||||
with store_lock(f"file:{os.path.realpath(self._path)}"):
|
||||
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
log_entry = {"timestamp": now, **kwargs}
|
||||
|
||||
if self._path.endswith(".json"):
|
||||
# Append log in JSON format
|
||||
try:
|
||||
# Try reading existing content to avoid overwriting
|
||||
with open(self._path, encoding="utf-8") as read_file:
|
||||
existing_data = json.load(read_file)
|
||||
existing_data.append(log_entry)
|
||||
except (json.JSONDecodeError, FileNotFoundError):
|
||||
# If no valid JSON or file doesn't exist, start with an empty list
|
||||
existing_data = [log_entry]
|
||||
if self._path.endswith(".json"):
|
||||
# Append log in JSON format
|
||||
try:
|
||||
# Try reading existing content to avoid overwriting
|
||||
with open(self._path, encoding="utf-8") as read_file:
|
||||
existing_data = json.load(read_file)
|
||||
existing_data.append(log_entry)
|
||||
except (json.JSONDecodeError, FileNotFoundError):
|
||||
# If no valid JSON or file doesn't exist, start with an empty list
|
||||
existing_data = [log_entry]
|
||||
|
||||
with open(self._path, "w", encoding="utf-8") as write_file:
|
||||
json.dump(existing_data, write_file, indent=4)
|
||||
write_file.write("\n")
|
||||
with open(self._path, "w", encoding="utf-8") as write_file:
|
||||
json.dump(existing_data, write_file, indent=4)
|
||||
write_file.write("\n")
|
||||
|
||||
else:
|
||||
# Append log in plain text format
|
||||
message = (
|
||||
f"{now}: "
|
||||
+ ", ".join([f'{key}="{value}"' for key, value in kwargs.items()])
|
||||
+ "\n"
|
||||
)
|
||||
with open(self._path, "a", encoding="utf-8") as file:
|
||||
file.write(message)
|
||||
else:
|
||||
# Append log in plain text format
|
||||
message = (
|
||||
f"{now}: "
|
||||
+ ", ".join(
|
||||
[f'{key}="{value}"' for key, value in kwargs.items()]
|
||||
)
|
||||
+ "\n"
|
||||
)
|
||||
with open(self._path, "a", encoding="utf-8") as file:
|
||||
file.write(message)
|
||||
|
||||
except Exception as e:
|
||||
raise ValueError(f"Failed to log message: {e!s}") from e
|
||||
@@ -153,8 +158,9 @@ class PickleHandler:
|
||||
Args:
|
||||
data: The data to be saved to the file.
|
||||
"""
|
||||
with open(self.file_path, "wb") as f:
|
||||
pickle.dump(obj=data, file=f)
|
||||
with store_lock(f"file:{os.path.realpath(self.file_path)}"):
|
||||
with open(self.file_path, "wb") as f:
|
||||
pickle.dump(obj=data, file=f)
|
||||
|
||||
def load(self) -> Any:
|
||||
"""Load the data from the specified file using pickle.
|
||||
@@ -162,13 +168,17 @@ class PickleHandler:
|
||||
Returns:
|
||||
The data loaded from the file.
|
||||
"""
|
||||
if not os.path.exists(self.file_path) or os.path.getsize(self.file_path) == 0:
|
||||
return {} # Return an empty dictionary if the file does not exist or is empty
|
||||
with store_lock(f"file:{os.path.realpath(self.file_path)}"):
|
||||
if (
|
||||
not os.path.exists(self.file_path)
|
||||
or os.path.getsize(self.file_path) == 0
|
||||
):
|
||||
return {}
|
||||
|
||||
with open(self.file_path, "rb") as file:
|
||||
try:
|
||||
return pickle.load(file) # noqa: S301
|
||||
except EOFError:
|
||||
return {} # Return an empty dictionary if the file is empty or corrupted
|
||||
except Exception:
|
||||
raise # Raise any other exceptions that occur during loading
|
||||
with open(self.file_path, "rb") as file:
|
||||
try:
|
||||
return pickle.load(file) # noqa: S301
|
||||
except EOFError:
|
||||
return {}
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
@@ -5,6 +5,7 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
from collections.abc import Coroutine
|
||||
import concurrent.futures
|
||||
import contextvars
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, TypeVar
|
||||
from uuid import UUID
|
||||
@@ -46,8 +47,9 @@ def _run_sync(coro: Coroutine[None, None, T]) -> T:
|
||||
"""
|
||||
try:
|
||||
asyncio.get_running_loop()
|
||||
ctx = contextvars.copy_context()
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
|
||||
future = executor.submit(asyncio.run, coro)
|
||||
future = executor.submit(ctx.run, asyncio.run, coro)
|
||||
return future.result()
|
||||
except RuntimeError:
|
||||
return asyncio.run(coro)
|
||||
|
||||
@@ -100,7 +100,12 @@ class I18N(BaseModel):
|
||||
def retrieve(
|
||||
self,
|
||||
kind: Literal[
|
||||
"slices", "errors", "tools", "reasoning", "hierarchical_manager_agent", "memory"
|
||||
"slices",
|
||||
"errors",
|
||||
"tools",
|
||||
"reasoning",
|
||||
"hierarchical_manager_agent",
|
||||
"memory",
|
||||
],
|
||||
key: str,
|
||||
) -> str:
|
||||
|
||||
@@ -657,7 +657,10 @@ def _json_schema_to_pydantic_field(
|
||||
A tuple of (type, Field) for use with create_model.
|
||||
"""
|
||||
type_ = _json_schema_to_pydantic_type(
|
||||
json_schema, root_schema, name_=name.title(), enrich_descriptions=enrich_descriptions
|
||||
json_schema,
|
||||
root_schema,
|
||||
name_=name.title(),
|
||||
enrich_descriptions=enrich_descriptions,
|
||||
)
|
||||
is_required = name in required
|
||||
|
||||
@@ -806,7 +809,10 @@ def _json_schema_to_pydantic_type(
|
||||
if ref:
|
||||
ref_schema = _resolve_ref(ref, root_schema)
|
||||
return _json_schema_to_pydantic_type(
|
||||
ref_schema, root_schema, name_=name_, enrich_descriptions=enrich_descriptions
|
||||
ref_schema,
|
||||
root_schema,
|
||||
name_=name_,
|
||||
enrich_descriptions=enrich_descriptions,
|
||||
)
|
||||
|
||||
enum_values = json_schema.get("enum")
|
||||
@@ -835,12 +841,16 @@ def _json_schema_to_pydantic_type(
|
||||
if all_of_schemas:
|
||||
if len(all_of_schemas) == 1:
|
||||
return _json_schema_to_pydantic_type(
|
||||
all_of_schemas[0], root_schema, name_=name_,
|
||||
all_of_schemas[0],
|
||||
root_schema,
|
||||
name_=name_,
|
||||
enrich_descriptions=enrich_descriptions,
|
||||
)
|
||||
merged = _merge_all_of_schemas(all_of_schemas, root_schema)
|
||||
return _json_schema_to_pydantic_type(
|
||||
merged, root_schema, name_=name_,
|
||||
merged,
|
||||
root_schema,
|
||||
name_=name_,
|
||||
enrich_descriptions=enrich_descriptions,
|
||||
)
|
||||
|
||||
@@ -858,7 +868,9 @@ def _json_schema_to_pydantic_type(
|
||||
items_schema = json_schema.get("items")
|
||||
if items_schema:
|
||||
item_type = _json_schema_to_pydantic_type(
|
||||
items_schema, root_schema, name_=name_,
|
||||
items_schema,
|
||||
root_schema,
|
||||
name_=name_,
|
||||
enrich_descriptions=enrich_descriptions,
|
||||
)
|
||||
return list[item_type] # type: ignore[valid-type]
|
||||
@@ -870,7 +882,8 @@ def _json_schema_to_pydantic_type(
|
||||
if json_schema_.get("title") is None:
|
||||
json_schema_["title"] = name_ or "DynamicModel"
|
||||
return create_model_from_schema(
|
||||
json_schema_, root_schema=root_schema,
|
||||
json_schema_,
|
||||
root_schema=root_schema,
|
||||
enrich_descriptions=enrich_descriptions,
|
||||
)
|
||||
return dict
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import asyncio
|
||||
from collections.abc import AsyncIterator, Callable, Iterator
|
||||
import contextvars
|
||||
import queue
|
||||
import threading
|
||||
from typing import Any, NamedTuple
|
||||
@@ -240,7 +241,8 @@ def create_chunk_generator(
|
||||
Yields:
|
||||
StreamChunk objects as they arrive.
|
||||
"""
|
||||
thread = threading.Thread(target=run_func, daemon=True)
|
||||
ctx = contextvars.copy_context()
|
||||
thread = threading.Thread(target=ctx.run, args=(run_func,), daemon=True)
|
||||
thread.start()
|
||||
|
||||
try:
|
||||
|
||||
@@ -1,828 +1,109 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: !!binary |
|
||||
CvP7AQokCiIKDHNlcnZpY2UubmFtZRISChBjcmV3QUktdGVsZW1ldHJ5Esn7AQoSChBjcmV3YWku
|
||||
dGVsZW1ldHJ5Ep4HChBGdupVRwCZRqXxk3FnMwCbEghSR8rOc1qkfCoMQ3JldyBDcmVhdGVkMAE5
|
||||
8GzO7sagGhhBOAHe7sagGhhKGgoOY3Jld2FpX3ZlcnNpb24SCAoGMC45NS4wShoKDnB5dGhvbl92
|
||||
ZXJzaW9uEggKBjMuMTIuN0ouCghjcmV3X2tleRIiCiBjOTdiNWZlYjVkMWI2NmJiNTkwMDZhYWEw
|
||||
MWEyOWNkNkoxCgdjcmV3X2lkEiYKJDk1NGM2OTJmLTc5Y2ItNGZlZi05NjNkLWUyMGRkMjFhMjAw
|
||||
MUocCgxjcmV3X3Byb2Nlc3MSDAoKc2VxdWVudGlhbEoRCgtjcmV3X21lbW9yeRICEABKGgoUY3Jl
|
||||
d19udW1iZXJfb2ZfdGFza3MSAhgBShsKFWNyZXdfbnVtYmVyX29mX2FnZW50cxICGAFKzAIKC2Ny
|
||||
ZXdfYWdlbnRzErwCCrkCW3sia2V5IjogIjA3ZDk5YjYzMDQxMWQzNWZkOTA0N2E1MzJkNTNkZGE3
|
||||
IiwgImlkIjogImQ5ZjkyYTBlLTVlZTYtNGY0NS04NzZiLWIwOWMyZTcwZWZkZiIsICJyb2xlIjog
|
||||
IlJlc2VhcmNoZXIiLCAidmVyYm9zZT8iOiBmYWxzZSwgIm1heF9pdGVyIjogMjAsICJtYXhfcnBt
|
||||
IjogbnVsbCwgImZ1bmN0aW9uX2NhbGxpbmdfbGxtIjogIiIsICJsbG0iOiAiZ3B0LTRvIiwgImRl
|
||||
bGVnYXRpb25fZW5hYmxlZD8iOiBmYWxzZSwgImFsbG93X2NvZGVfZXhlY3V0aW9uPyI6IGZhbHNl
|
||||
LCAibWF4X3JldHJ5X2xpbWl0IjogMiwgInRvb2xzX25hbWVzIjogW119XUr/AQoKY3Jld190YXNr
|
||||
cxLwAQrtAVt7ImtleSI6ICI2Mzk5NjUxN2YzZjNmMWM5NGQ2YmI2MTdhYTBiMWM0ZiIsICJpZCI6
|
||||
ICIzZDc0NDlkYi0wMzU3LTQ3NTMtOGNmNS03NGY2ZmMzMGEwYTkiLCAiYXN5bmNfZXhlY3V0aW9u
|
||||
PyI6IGZhbHNlLCAiaHVtYW5faW5wdXQ/IjogZmFsc2UsICJhZ2VudF9yb2xlIjogIlJlc2VhcmNo
|
||||
ZXIiLCAiYWdlbnRfa2V5IjogIjA3ZDk5YjYzMDQxMWQzNWZkOTA0N2E1MzJkNTNkZGE3IiwgInRv
|
||||
b2xzX25hbWVzIjogW119XXoCGAGFAQABAAASjgIKEP1sZDWz95ImNTj+qx9ckqUSCAmsHrq64Y/u
|
||||
KgxUYXNrIENyZWF0ZWQwATnQXu3uxqAaGEFgxO3uxqAaGEouCghjcmV3X2tleRIiCiBjOTdiNWZl
|
||||
YjVkMWI2NmJiNTkwMDZhYWEwMWEyOWNkNkoxCgdjcmV3X2lkEiYKJDk1NGM2OTJmLTc5Y2ItNGZl
|
||||
Zi05NjNkLWUyMGRkMjFhMjAwMUouCgh0YXNrX2tleRIiCiA2Mzk5NjUxN2YzZjNmMWM5NGQ2YmI2
|
||||
MTdhYTBiMWM0ZkoxCgd0YXNrX2lkEiYKJDNkNzQ0OWRiLTAzNTctNDc1My04Y2Y1LTc0ZjZmYzMw
|
||||
YTBhOXoCGAGFAQABAAASngcKEBNuju55KsgJoN1+Y7gEx24SCCoSNPvs01ScKgxDcmV3IENyZWF0
|
||||
ZWQwATlIpr3wxqAaGEHwVMbwxqAaGEoaCg5jcmV3YWlfdmVyc2lvbhIICgYwLjk1LjBKGgoOcHl0
|
||||
aG9uX3ZlcnNpb24SCAoGMy4xMi43Si4KCGNyZXdfa2V5EiIKIDhjMjc1MmY0OWU1YjlkMmI2OGNi
|
||||
MzVjYWM4ZmNjODZkSjEKB2NyZXdfaWQSJgokMTY2ODBmZjMtMjM1Yy00MzZlLTk2MWMtZGNhYWNh
|
||||
YTFiMjA4ShwKDGNyZXdfcHJvY2VzcxIMCgpzZXF1ZW50aWFsShEKC2NyZXdfbWVtb3J5EgIQAEoa
|
||||
ChRjcmV3X251bWJlcl9vZl90YXNrcxICGAFKGwoVY3Jld19udW1iZXJfb2ZfYWdlbnRzEgIYAUrM
|
||||
AgoLY3Jld19hZ2VudHMSvAIKuQJbeyJrZXkiOiAiOGJkMjEzOWI1OTc1MTgxNTA2ZTQxZmQ5YzQ1
|
||||
NjNkNzUiLCAiaWQiOiAiMzY5NmM3ZDktNjcyYS00NmIzLWJlMGMtMzNmNjI2YjEwMGU3IiwgInJv
|
||||
bGUiOiAiUmVzZWFyY2hlciIsICJ2ZXJib3NlPyI6IGZhbHNlLCAibWF4X2l0ZXIiOiAyMCwgIm1h
|
||||
eF9ycG0iOiBudWxsLCAiZnVuY3Rpb25fY2FsbGluZ19sbG0iOiAiIiwgImxsbSI6ICJncHQtNG8i
|
||||
LCAiZGVsZWdhdGlvbl9lbmFibGVkPyI6IGZhbHNlLCAiYWxsb3dfY29kZV9leGVjdXRpb24/Ijog
|
||||
ZmFsc2UsICJtYXhfcmV0cnlfbGltaXQiOiAyLCAidG9vbHNfbmFtZXMiOiBbXX1dSv8BCgpjcmV3
|
||||
X3Rhc2tzEvABCu0BW3sia2V5IjogIjBkNjg1YTIxOTk0ZDk0OTA5N2JjNWE1NmQ3MzdlNmQxIiwg
|
||||
ImlkIjogIjIzYWM1MzA1LTg5YTUtNDM1NC1hODUyLTNmNGNlNDk4NjY4NCIsICJhc3luY19leGVj
|
||||
dXRpb24/IjogZmFsc2UsICJodW1hbl9pbnB1dD8iOiBmYWxzZSwgImFnZW50X3JvbGUiOiAiUmVz
|
||||
ZWFyY2hlciIsICJhZ2VudF9rZXkiOiAiOGJkMjEzOWI1OTc1MTgxNTA2ZTQxZmQ5YzQ1NjNkNzUi
|
||||
LCAidG9vbHNfbmFtZXMiOiBbXX1degIYAYUBAAEAABKOAgoQt0jLLt+z7mZzw/JaxaWi4xII/o7T
|
||||
QUAqVu8qDFRhc2sgQ3JlYXRlZDABOYg71PDGoBoYQZCN1PDGoBoYSi4KCGNyZXdfa2V5EiIKIDhj
|
||||
Mjc1MmY0OWU1YjlkMmI2OGNiMzVjYWM4ZmNjODZkSjEKB2NyZXdfaWQSJgokMTY2ODBmZjMtMjM1
|
||||
Yy00MzZlLTk2MWMtZGNhYWNhYTFiMjA4Si4KCHRhc2tfa2V5EiIKIDBkNjg1YTIxOTk0ZDk0OTA5
|
||||
N2JjNWE1NmQ3MzdlNmQxSjEKB3Rhc2tfaWQSJgokMjNhYzUzMDUtODlhNS00MzU0LWE4NTItM2Y0
|
||||
Y2U0OTg2Njg0egIYAYUBAAEAABKeBwoQAddeR+5jHI68iED9tmGToRIIqsyiA/tKs2QqDENyZXcg
|
||||
Q3JlYXRlZDABOcC+UPrGoBoYQchXWvrGoBoYShoKDmNyZXdhaV92ZXJzaW9uEggKBjAuOTUuMEoa
|
||||
Cg5weXRob25fdmVyc2lvbhIICgYzLjEyLjdKLgoIY3Jld19rZXkSIgogYjY3MzY4NmZjODIyYzIw
|
||||
M2M3ZTg3OWM2NzU0MjQ2OTlKMQoHY3Jld19pZBImCiRmYjJjNzYwZi00ZTdhLTQ0ZDctOWI4My1i
|
||||
NDA3MjY5YjVjZDRKHAoMY3Jld19wcm9jZXNzEgwKCnNlcXVlbnRpYWxKEQoLY3Jld19tZW1vcnkS
|
||||
AhAAShoKFGNyZXdfbnVtYmVyX29mX3Rhc2tzEgIYAUobChVjcmV3X251bWJlcl9vZl9hZ2VudHMS
|
||||
AhgBSswCCgtjcmV3X2FnZW50cxK8Agq5Alt7ImtleSI6ICJiNTljZjc3YjZlNzY1ODQ4NzBlYjFj
|
||||
Mzg4MjNkN2UyOCIsICJpZCI6ICJhMTA3Y2M4My1jZjM0LTRhMDctYWFmNi1lNzA4MTU0MmNiOTUi
|
||||
LCAicm9sZSI6ICJSZXNlYXJjaGVyIiwgInZlcmJvc2U/IjogZmFsc2UsICJtYXhfaXRlciI6IDIw
|
||||
LCAibWF4X3JwbSI6IG51bGwsICJmdW5jdGlvbl9jYWxsaW5nX2xsbSI6ICIiLCAibGxtIjogImdw
|
||||
dC00byIsICJkZWxlZ2F0aW9uX2VuYWJsZWQ/IjogZmFsc2UsICJhbGxvd19jb2RlX2V4ZWN1dGlv
|
||||
bj8iOiBmYWxzZSwgIm1heF9yZXRyeV9saW1pdCI6IDIsICJ0b29sc19uYW1lcyI6IFtdfV1K/wEK
|
||||
CmNyZXdfdGFza3MS8AEK7QFbeyJrZXkiOiAiYTVlNWM1OGNlYTFiOWQwMDMzMmU2ODQ0MWQzMjdi
|
||||
ZGYiLCAiaWQiOiAiNTYzNjc0NmQtNmQ4YS00YzBjLTgyNmEtNDA2YzRlMzc0MTg5IiwgImFzeW5j
|
||||
X2V4ZWN1dGlvbj8iOiBmYWxzZSwgImh1bWFuX2lucHV0PyI6IGZhbHNlLCAiYWdlbnRfcm9sZSI6
|
||||
ICJSZXNlYXJjaGVyIiwgImFnZW50X2tleSI6ICJiNTljZjc3YjZlNzY1ODQ4NzBlYjFjMzg4MjNk
|
||||
N2UyOCIsICJ0b29sc19uYW1lcyI6IFtdfV16AhgBhQEAAQAAEo4CChDxrID3kZmdkWC//z9+mfuy
|
||||
EgjUxsn2MojVPioMVGFzayBDcmVhdGVkMAE5IIRs+sagGhhB4OFs+sagGhhKLgoIY3Jld19rZXkS
|
||||
IgogYjY3MzY4NmZjODIyYzIwM2M3ZTg3OWM2NzU0MjQ2OTlKMQoHY3Jld19pZBImCiRmYjJjNzYw
|
||||
Zi00ZTdhLTQ0ZDctOWI4My1iNDA3MjY5YjVjZDRKLgoIdGFza19rZXkSIgogYTVlNWM1OGNlYTFi
|
||||
OWQwMDMzMmU2ODQ0MWQzMjdiZGZKMQoHdGFza19pZBImCiQ1NjM2NzQ2ZC02ZDhhLTRjMGMtODI2
|
||||
YS00MDZjNGUzNzQxODl6AhgBhQEAAQAAErgJChCvyf8lGSXM52eSUv8BPeh1EghI6rK/hduMWSoM
|
||||
Q3JldyBDcmVhdGVkMAE5mJtE/MagGhhB+NhM/MagGhhKGgoOY3Jld2FpX3ZlcnNpb24SCAoGMC45
|
||||
NS4wShoKDnB5dGhvbl92ZXJzaW9uEggKBjMuMTIuN0ouCghjcmV3X2tleRIiCiBlM2ZkYTBmMzEx
|
||||
MGZlODBiMTg5NDdjMDE0NzE0MzBhNEoxCgdjcmV3X2lkEiYKJDQ5ZWRjNGIwLWZlNzctNDc0Yy1i
|
||||
OGE0LTljMDlkNDUzMWIxY0oeCgxjcmV3X3Byb2Nlc3MSDgoMaGllcmFyY2hpY2FsShEKC2NyZXdf
|
||||
bWVtb3J5EgIQAEoaChRjcmV3X251bWJlcl9vZl90YXNrcxICGAFKGwoVY3Jld19udW1iZXJfb2Zf
|
||||
YWdlbnRzEgIYAkqIBQoLY3Jld19hZ2VudHMS+AQK9QRbeyJrZXkiOiAiOGJkMjEzOWI1OTc1MTgx
|
||||
NTA2ZTQxZmQ5YzQ1NjNkNzUiLCAiaWQiOiAiMzY5NmM3ZDktNjcyYS00NmIzLWJlMGMtMzNmNjI2
|
||||
YjEwMGU3IiwgInJvbGUiOiAiUmVzZWFyY2hlciIsICJ2ZXJib3NlPyI6IGZhbHNlLCAibWF4X2l0
|
||||
ZXIiOiAyMCwgIm1heF9ycG0iOiBudWxsLCAiZnVuY3Rpb25fY2FsbGluZ19sbG0iOiAiIiwgImxs
|
||||
bSI6ICJncHQtNG8iLCAiZGVsZWdhdGlvbl9lbmFibGVkPyI6IGZhbHNlLCAiYWxsb3dfY29kZV9l
|
||||
eGVjdXRpb24/IjogZmFsc2UsICJtYXhfcmV0cnlfbGltaXQiOiAyLCAidG9vbHNfbmFtZXMiOiBb
|
||||
XX0sIHsia2V5IjogIjlhNTAxNWVmNDg5NWRjNjI3OGQ1NDgxOGJhNDQ2YWY3IiwgImlkIjogImE5
|
||||
OTRlNjZlLWE5OTEtNDRhNi04OTIxLWE4OGQ0M2QyNjZiYyIsICJyb2xlIjogIlNlbmlvciBXcml0
|
||||
ZXIiLCAidmVyYm9zZT8iOiBmYWxzZSwgIm1heF9pdGVyIjogMjAsICJtYXhfcnBtIjogbnVsbCwg
|
||||
ImZ1bmN0aW9uX2NhbGxpbmdfbGxtIjogIiIsICJsbG0iOiAiZ3B0LTRvIiwgImRlbGVnYXRpb25f
|
||||
ZW5hYmxlZD8iOiBmYWxzZSwgImFsbG93X2NvZGVfZXhlY3V0aW9uPyI6IGZhbHNlLCAibWF4X3Jl
|
||||
dHJ5X2xpbWl0IjogMiwgInRvb2xzX25hbWVzIjogW119XUrbAQoKY3Jld190YXNrcxLMAQrJAVt7
|
||||
ImtleSI6ICI1ZmE2NWMwNmE5ZTMxZjJjNjk1NDMyNjY4YWNkNjJkZCIsICJpZCI6ICJiOTY5MGI1
|
||||
OC1hYmNhLTRjYzktOGZlYS01ZTZmNDZjNmQ5ZDUiLCAiYXN5bmNfZXhlY3V0aW9uPyI6IGZhbHNl
|
||||
LCAiaHVtYW5faW5wdXQ/IjogZmFsc2UsICJhZ2VudF9yb2xlIjogIk5vbmUiLCAiYWdlbnRfa2V5
|
||||
IjogbnVsbCwgInRvb2xzX25hbWVzIjogW119XXoCGAGFAQABAAASuAkKECCrkzgLIi2bqMUA6kHF
|
||||
B1ESCFsUbfXKnCROKgxDcmV3IENyZWF0ZWQwATnAlbP8xqAaGEGwPrv8xqAaGEoaCg5jcmV3YWlf
|
||||
dmVyc2lvbhIICgYwLjk1LjBKGgoOcHl0aG9uX3ZlcnNpb24SCAoGMy4xMi43Si4KCGNyZXdfa2V5
|
||||
EiIKIGUzZmRhMGYzMTEwZmU4MGIxODk0N2MwMTQ3MTQzMGE0SjEKB2NyZXdfaWQSJgokNDJlMGQ1
|
||||
MmYtYWVjYS00MTMzLTlmMDItZDZiOGU0OTRkYjYxSh4KDGNyZXdfcHJvY2VzcxIOCgxoaWVyYXJj
|
||||
aGljYWxKEQoLY3Jld19tZW1vcnkSAhAAShoKFGNyZXdfbnVtYmVyX29mX3Rhc2tzEgIYAUobChVj
|
||||
cmV3X251bWJlcl9vZl9hZ2VudHMSAhgCSogFCgtjcmV3X2FnZW50cxL4BAr1BFt7ImtleSI6ICI4
|
||||
YmQyMTM5YjU5NzUxODE1MDZlNDFmZDljNDU2M2Q3NSIsICJpZCI6ICIzNjk2YzdkOS02NzJhLTQ2
|
||||
YjMtYmUwYy0zM2Y2MjZiMTAwZTciLCAicm9sZSI6ICJSZXNlYXJjaGVyIiwgInZlcmJvc2U/Ijog
|
||||
ZmFsc2UsICJtYXhfaXRlciI6IDIwLCAibWF4X3JwbSI6IG51bGwsICJmdW5jdGlvbl9jYWxsaW5n
|
||||
X2xsbSI6ICIiLCAibGxtIjogImdwdC00byIsICJkZWxlZ2F0aW9uX2VuYWJsZWQ/IjogZmFsc2Us
|
||||
ICJhbGxvd19jb2RlX2V4ZWN1dGlvbj8iOiBmYWxzZSwgIm1heF9yZXRyeV9saW1pdCI6IDIsICJ0
|
||||
b29sc19uYW1lcyI6IFtdfSwgeyJrZXkiOiAiOWE1MDE1ZWY0ODk1ZGM2Mjc4ZDU0ODE4YmE0NDZh
|
||||
ZjciLCAiaWQiOiAiYTk5NGU2NmUtYTk5MS00NGE2LTg5MjEtYTg4ZDQzZDI2NmJjIiwgInJvbGUi
|
||||
OiAiU2VuaW9yIFdyaXRlciIsICJ2ZXJib3NlPyI6IGZhbHNlLCAibWF4X2l0ZXIiOiAyMCwgIm1h
|
||||
eF9ycG0iOiBudWxsLCAiZnVuY3Rpb25fY2FsbGluZ19sbG0iOiAiIiwgImxsbSI6ICJncHQtNG8i
|
||||
LCAiZGVsZWdhdGlvbl9lbmFibGVkPyI6IGZhbHNlLCAiYWxsb3dfY29kZV9leGVjdXRpb24/Ijog
|
||||
ZmFsc2UsICJtYXhfcmV0cnlfbGltaXQiOiAyLCAidG9vbHNfbmFtZXMiOiBbXX1dStsBCgpjcmV3
|
||||
X3Rhc2tzEswBCskBW3sia2V5IjogIjVmYTY1YzA2YTllMzFmMmM2OTU0MzI2NjhhY2Q2MmRkIiwg
|
||||
ImlkIjogImM3MGNmMzliLTE2YzktNDNiOC1hN2VhLTY5MTgzZmZmZDg5ZiIsICJhc3luY19leGVj
|
||||
dXRpb24/IjogZmFsc2UsICJodW1hbl9pbnB1dD8iOiBmYWxzZSwgImFnZW50X3JvbGUiOiAiTm9u
|
||||
ZSIsICJhZ2VudF9rZXkiOiBudWxsLCAidG9vbHNfbmFtZXMiOiBbXX1degIYAYUBAAEAABLKCwoQ
|
||||
Nu3FGKmDx1jRbaca6HH3TRIIb9vd1api6NYqDENyZXcgQ3JlYXRlZDABOaiMR/3GoBoYQRjxT/3G
|
||||
oBoYShoKDmNyZXdhaV92ZXJzaW9uEggKBjAuOTUuMEoaCg5weXRob25fdmVyc2lvbhIICgYzLjEy
|
||||
LjdKLgoIY3Jld19rZXkSIgogZDM4NDZjOWQyNzZlOGU2ZTQzZTMxZjYxNzYzNTdiNGZKMQoHY3Jl
|
||||
d19pZBImCiQ2MDE5NzNhNy04NDlmLTQ4ZWQtOGM4MS04YzY5N2QyY2ViNGRKHAoMY3Jld19wcm9j
|
||||
ZXNzEgwKCnNlcXVlbnRpYWxKEQoLY3Jld19tZW1vcnkSAhAAShoKFGNyZXdfbnVtYmVyX29mX3Rh
|
||||
c2tzEgIYAkobChVjcmV3X251bWJlcl9vZl9hZ2VudHMSAhgCSogFCgtjcmV3X2FnZW50cxL4BAr1
|
||||
BFt7ImtleSI6ICI4YmQyMTM5YjU5NzUxODE1MDZlNDFmZDljNDU2M2Q3NSIsICJpZCI6ICIzNjk2
|
||||
YzdkOS02NzJhLTQ2YjMtYmUwYy0zM2Y2MjZiMTAwZTciLCAicm9sZSI6ICJSZXNlYXJjaGVyIiwg
|
||||
InZlcmJvc2U/IjogZmFsc2UsICJtYXhfaXRlciI6IDIwLCAibWF4X3JwbSI6IG51bGwsICJmdW5j
|
||||
dGlvbl9jYWxsaW5nX2xsbSI6ICIiLCAibGxtIjogImdwdC00byIsICJkZWxlZ2F0aW9uX2VuYWJs
|
||||
ZWQ/IjogZmFsc2UsICJhbGxvd19jb2RlX2V4ZWN1dGlvbj8iOiBmYWxzZSwgIm1heF9yZXRyeV9s
|
||||
aW1pdCI6IDIsICJ0b29sc19uYW1lcyI6IFtdfSwgeyJrZXkiOiAiOWE1MDE1ZWY0ODk1ZGM2Mjc4
|
||||
ZDU0ODE4YmE0NDZhZjciLCAiaWQiOiAiYTk5NGU2NmUtYTk5MS00NGE2LTg5MjEtYTg4ZDQzZDI2
|
||||
NmJjIiwgInJvbGUiOiAiU2VuaW9yIFdyaXRlciIsICJ2ZXJib3NlPyI6IGZhbHNlLCAibWF4X2l0
|
||||
ZXIiOiAyMCwgIm1heF9ycG0iOiBudWxsLCAiZnVuY3Rpb25fY2FsbGluZ19sbG0iOiAiIiwgImxs
|
||||
bSI6ICJncHQtNG8iLCAiZGVsZWdhdGlvbl9lbmFibGVkPyI6IGZhbHNlLCAiYWxsb3dfY29kZV9l
|
||||
eGVjdXRpb24/IjogZmFsc2UsICJtYXhfcmV0cnlfbGltaXQiOiAyLCAidG9vbHNfbmFtZXMiOiBb
|
||||
XX1dSu8DCgpjcmV3X3Rhc2tzEuADCt0DW3sia2V5IjogImU5ZTZiNzJhYWMzMjY0NTlkZDcwNjhm
|
||||
MGIxNzE3YzFjIiwgImlkIjogImYzNGM5ZGZjLWU4NzYtNDkzNS04NTNmLTMyM2EwYzhhZGViMiIs
|
||||
ICJhc3luY19leGVjdXRpb24/IjogZmFsc2UsICJodW1hbl9pbnB1dD8iOiBmYWxzZSwgImFnZW50
|
||||
X3JvbGUiOiAiUmVzZWFyY2hlciIsICJhZ2VudF9rZXkiOiAiOGJkMjEzOWI1OTc1MTgxNTA2ZTQx
|
||||
ZmQ5YzQ1NjNkNzUiLCAidG9vbHNfbmFtZXMiOiBbXX0sIHsia2V5IjogImVlZWU3ZTczZDVkZjY2
|
||||
ZDQ4ZDJkODA3YmFmZjg3NGYzIiwgImlkIjogImNjOGMxZGQ0LTUxNzktNDdlMC1iMTk0LTU3NmNh
|
||||
MjFkZjllOCIsICJhc3luY19leGVjdXRpb24/IjogZmFsc2UsICJodW1hbl9pbnB1dD8iOiBmYWxz
|
||||
ZSwgImFnZW50X3JvbGUiOiAiU2VuaW9yIFdyaXRlciIsICJhZ2VudF9rZXkiOiAiOWE1MDE1ZWY0
|
||||
ODk1ZGM2Mjc4ZDU0ODE4YmE0NDZhZjciLCAidG9vbHNfbmFtZXMiOiBbXX1degIYAYUBAAEAABKm
|
||||
BwoQYZWMzWnoYys7S/fnI87iGRIIla+Vilm2/HgqDENyZXcgQ3JlYXRlZDABOaDT6f3GoBoYQZB8
|
||||
8f3GoBoYShoKDmNyZXdhaV92ZXJzaW9uEggKBjAuOTUuMEoaCg5weXRob25fdmVyc2lvbhIICgYz
|
||||
LjEyLjdKLgoIY3Jld19rZXkSIgogNjczOGFkNWI4Y2IzZTZmMWMxYzkzNTBiOTZjMmU2NzhKMQoH
|
||||
Y3Jld19pZBImCiRjYjJmYWQ2NS1jZmVlLTQ5MjMtYmE4ZS1jYzllYTM4YmRlZDVKHAoMY3Jld19w
|
||||
cm9jZXNzEgwKCnNlcXVlbnRpYWxKEQoLY3Jld19tZW1vcnkSAhAAShoKFGNyZXdfbnVtYmVyX29m
|
||||
X3Rhc2tzEgIYAUobChVjcmV3X251bWJlcl9vZl9hZ2VudHMSAhgBStACCgtjcmV3X2FnZW50cxLA
|
||||
Agq9Alt7ImtleSI6ICI1MTJhNmRjMzc5ZjY2YjIxZWVhYjI0ZTYzNDgzNmY3MiIsICJpZCI6ICJl
|
||||
ZmM1ZmYyNC1lNGRlLTQwMDctOTE0Ni03MzQ2ODkyMzMxNmEiLCAicm9sZSI6ICJDb250ZW50IFdy
|
||||
aXRlciIsICJ2ZXJib3NlPyI6IGZhbHNlLCAibWF4X2l0ZXIiOiAyMCwgIm1heF9ycG0iOiBudWxs
|
||||
LCAiZnVuY3Rpb25fY2FsbGluZ19sbG0iOiAiIiwgImxsbSI6ICJncHQtNG8iLCAiZGVsZWdhdGlv
|
||||
bl9lbmFibGVkPyI6IGZhbHNlLCAiYWxsb3dfY29kZV9leGVjdXRpb24/IjogZmFsc2UsICJtYXhf
|
||||
cmV0cnlfbGltaXQiOiAyLCAidG9vbHNfbmFtZXMiOiBbXX1dSoMCCgpjcmV3X3Rhc2tzEvQBCvEB
|
||||
W3sia2V5IjogIjM0NzcwNzZiZTNhZjcxMzA0NjJlZGFhMmViOGEwNDhlIiwgImlkIjogImI1YTU1
|
||||
ZDIxLWM0YWQtNGY3MS1hNzlmLTc5MmI3MzcwZDM0MSIsICJhc3luY19leGVjdXRpb24/IjogZmFs
|
||||
c2UsICJodW1hbl9pbnB1dD8iOiBmYWxzZSwgImFnZW50X3JvbGUiOiAiQ29udGVudCBXcml0ZXIi
|
||||
LCAiYWdlbnRfa2V5IjogIjUxMmE2ZGMzNzlmNjZiMjFlZWFiMjRlNjM0ODM2ZjcyIiwgInRvb2xz
|
||||
X25hbWVzIjogW119XXoCGAGFAQABAAASjg8KEPffWTWZFpn8wcrgD+eyhrMSCHU6W3vsK6dIKgxD
|
||||
cmV3IENyZWF0ZWQwATmAXFj+xqAaGEHQ72D+xqAaGEoaCg5jcmV3YWlfdmVyc2lvbhIICgYwLjk1
|
||||
LjBKGgoOcHl0aG9uX3ZlcnNpb24SCAoGMy4xMi43Si4KCGNyZXdfa2V5EiIKIDRhY2I5MzNmZThk
|
||||
ZTRjZDU3NzJlZGIwZTgyMDZlMjhmSjEKB2NyZXdfaWQSJgokZjQ4NDAzYjUtZjRjMi00NjA4LWE1
|
||||
YzYtMjc4NGU5ZTY0MDNlShwKDGNyZXdfcHJvY2VzcxIMCgpzZXF1ZW50aWFsShEKC2NyZXdfbWVt
|
||||
b3J5EgIQAEoaChRjcmV3X251bWJlcl9vZl90YXNrcxICGARKGwoVY3Jld19udW1iZXJfb2ZfYWdl
|
||||
bnRzEgIYAkqBBQoLY3Jld19hZ2VudHMS8QQK7gRbeyJrZXkiOiAiMmJlZmZkY2FjNjVjY2VhYTY1
|
||||
Mzk2ZjJjN2Y1NjhlNmEiLCAiaWQiOiAiNzlkY2E1NjgtOTUxNy00ZWM0LThkODctMDMxZWFlM2Ji
|
||||
OTk1IiwgInJvbGUiOiAiUmVzZWFyY2hlciIsICJ2ZXJib3NlPyI6IGZhbHNlLCAibWF4X2l0ZXIi
|
||||
OiAyMCwgIm1heF9ycG0iOiBudWxsLCAiZnVuY3Rpb25fY2FsbGluZ19sbG0iOiAiIiwgImxsbSI6
|
||||
ICJncHQtNG8iLCAiZGVsZWdhdGlvbl9lbmFibGVkPyI6IGZhbHNlLCAiYWxsb3dfY29kZV9leGVj
|
||||
dXRpb24/IjogZmFsc2UsICJtYXhfcmV0cnlfbGltaXQiOiAyLCAidG9vbHNfbmFtZXMiOiBbXX0s
|
||||
IHsia2V5IjogIjFjZGNhOGRlMDdiMjhkMDc0ZDc4NjQ3NDhiZGIxNzY3IiwgImlkIjogIjgzZWI3
|
||||
MGNkLWIzODEtNDYwMy05Nzg5LTkyN2IxYmNlYTU2ZCIsICJyb2xlIjogIldyaXRlciIsICJ2ZXJi
|
||||
b3NlPyI6IGZhbHNlLCAibWF4X2l0ZXIiOiAyMCwgIm1heF9ycG0iOiBudWxsLCAiZnVuY3Rpb25f
|
||||
Y2FsbGluZ19sbG0iOiAiIiwgImxsbSI6ICJncHQtNG8iLCAiZGVsZWdhdGlvbl9lbmFibGVkPyI6
|
||||
IGZhbHNlLCAiYWxsb3dfY29kZV9leGVjdXRpb24/IjogZmFsc2UsICJtYXhfcmV0cnlfbGltaXQi
|
||||
OiAyLCAidG9vbHNfbmFtZXMiOiBbXX1dSroHCgpjcmV3X3Rhc2tzEqsHCqgHW3sia2V5IjogImVi
|
||||
YWVhYTk2ZThjODU1N2YwNDYxNzM2ZDRiZWY5MzE3IiwgImlkIjogImRkMGVkMzgxLTZhNzUtNDVh
|
||||
My1iZGUyLTRlNzdiOTU0YmI2OCIsICJhc3luY19leGVjdXRpb24/IjogZmFsc2UsICJodW1hbl9p
|
||||
bnB1dD8iOiBmYWxzZSwgImFnZW50X3JvbGUiOiAiUmVzZWFyY2hlciIsICJhZ2VudF9rZXkiOiAi
|
||||
MmJlZmZkY2FjNjVjY2VhYTY1Mzk2ZjJjN2Y1NjhlNmEiLCAidG9vbHNfbmFtZXMiOiBbXX0sIHsi
|
||||
a2V5IjogIjYwZjM1MjI4ZWMxY2I3M2ZlZDM1ZDk5MTBhNmQ3OWYzIiwgImlkIjogImE0OGZmMzgx
|
||||
LTI2ZDEtNDVjNy04MGVkLWJlODM0NTkxYWIzYyIsICJhc3luY19leGVjdXRpb24/IjogZmFsc2Us
|
||||
ICJodW1hbl9pbnB1dD8iOiBmYWxzZSwgImFnZW50X3JvbGUiOiAiV3JpdGVyIiwgImFnZW50X2tl
|
||||
eSI6ICIxY2RjYThkZTA3YjI4ZDA3NGQ3ODY0NzQ4YmRiMTc2NyIsICJ0b29sc19uYW1lcyI6IFtd
|
||||
fSwgeyJrZXkiOiAiYmUyYTcxNGFjMzVlM2E2YjBhYmJhMjRjZWMyZTA0Y2MiLCAiaWQiOiAiMDkx
|
||||
YWE2YjMtZGYyMC00YTMzLTk1MzUtOGJiNDllMzlhMGQyIiwgImFzeW5jX2V4ZWN1dGlvbj8iOiBm
|
||||
YWxzZSwgImh1bWFuX2lucHV0PyI6IGZhbHNlLCAiYWdlbnRfcm9sZSI6ICJXcml0ZXIiLCAiYWdl
|
||||
bnRfa2V5IjogIjFjZGNhOGRlMDdiMjhkMDc0ZDc4NjQ3NDhiZGIxNzY3IiwgInRvb2xzX25hbWVz
|
||||
IjogW119LCB7ImtleSI6ICI0YTU2YTYyNzk4ODZhNmZlNThkNjc1NzgxZDFmNWFkOSIsICJpZCI6
|
||||
ICIxMDFlOGNhNC04MTk1LTQyNDYtYjg2Ny05ZjYxYzM1NWJjOGIiLCAiYXN5bmNfZXhlY3V0aW9u
|
||||
PyI6IGZhbHNlLCAiaHVtYW5faW5wdXQ/IjogZmFsc2UsICJhZ2VudF9yb2xlIjogIldyaXRlciIs
|
||||
ICJhZ2VudF9rZXkiOiAiMWNkY2E4ZGUwN2IyOGQwNzRkNzg2NDc0OGJkYjE3NjciLCAidG9vbHNf
|
||||
bmFtZXMiOiBbXX1degIYAYUBAAEAABKLCQoQgHmumMETjYmEZpveDu3dwBIIByVlUIAMTMEqDENy
|
||||
ZXcgQ3JlYXRlZDABOfgtEgDHoBoYQTC/GwDHoBoYShoKDmNyZXdhaV92ZXJzaW9uEggKBjAuOTUu
|
||||
MEoaCg5weXRob25fdmVyc2lvbhIICgYzLjEyLjdKLgoIY3Jld19rZXkSIgogODBjNzk4ZjYyMjhm
|
||||
MzJhNzQ4M2Y3MmFmZTM2NmVkY2FKMQoHY3Jld19pZBImCiQ0YzM3YTFhNS1lMzA5LTQ2N2EtYWJk
|
||||
ZC0zZDY1YThlNjY5ZjBKHAoMY3Jld19wcm9jZXNzEgwKCnNlcXVlbnRpYWxKEQoLY3Jld19tZW1v
|
||||
cnkSAhAAShoKFGNyZXdfbnVtYmVyX29mX3Rhc2tzEgIYAkobChVjcmV3X251bWJlcl9vZl9hZ2Vu
|
||||
dHMSAhgBSswCCgtjcmV3X2FnZW50cxK8Agq5Alt7ImtleSI6ICIzN2Q3MTNkM2RjZmFlMWRlNTNi
|
||||
NGUyZGFjNzU1M2ZkNyIsICJpZCI6ICJmNGY2NmQxMi01M2Q0LTQ2NTQtODRiZC1lMjJmYzk2ZDU0
|
||||
NTEiLCAicm9sZSI6ICJ0ZXN0X2FnZW50IiwgInZlcmJvc2U/IjogZmFsc2UsICJtYXhfaXRlciI6
|
||||
IDIwLCAibWF4X3JwbSI6IG51bGwsICJmdW5jdGlvbl9jYWxsaW5nX2xsbSI6ICIiLCAibGxtIjog
|
||||
ImdwdC00byIsICJkZWxlZ2F0aW9uX2VuYWJsZWQ/IjogZmFsc2UsICJhbGxvd19jb2RlX2V4ZWN1
|
||||
dGlvbj8iOiBmYWxzZSwgIm1heF9yZXRyeV9saW1pdCI6IDIsICJ0b29sc19uYW1lcyI6IFtdfV1K
|
||||
7AMKCmNyZXdfdGFza3MS3QMK2gNbeyJrZXkiOiAiY2M0YTQyYzE4NmVlMWEyZTY2YjAyOGVjNWI3
|
||||
MmJkNGUiLCAiaWQiOiAiMmUyMmZiMDMtMzIxMS00NTgxLTkzN2EtZjY1Zjk5MjY3ZmIyIiwgImFz
|
||||
eW5jX2V4ZWN1dGlvbj8iOiBmYWxzZSwgImh1bWFuX2lucHV0PyI6IGZhbHNlLCAiYWdlbnRfcm9s
|
||||
ZSI6ICJ0ZXN0X2FnZW50IiwgImFnZW50X2tleSI6ICIzN2Q3MTNkM2RjZmFlMWRlNTNiNGUyZGFj
|
||||
NzU1M2ZkNyIsICJ0b29sc19uYW1lcyI6IFtdfSwgeyJrZXkiOiAiNzRlNmIyNDQ5YzQ1NzRhY2Jj
|
||||
MmJmNDk3MjczYTVjYzEiLCAiaWQiOiAiODIzYmRlYzUtMTRkMS00ZDdjLWJkYWMtODkzNTY1YmFi
|
||||
YmM1IiwgImFzeW5jX2V4ZWN1dGlvbj8iOiBmYWxzZSwgImh1bWFuX2lucHV0PyI6IGZhbHNlLCAi
|
||||
YWdlbnRfcm9sZSI6ICJ0ZXN0X2FnZW50IiwgImFnZW50X2tleSI6ICIzN2Q3MTNkM2RjZmFlMWRl
|
||||
NTNiNGUyZGFjNzU1M2ZkNyIsICJ0b29sc19uYW1lcyI6IFtdfV16AhgBhQEAAQAAEo4CChDXwUEa
|
||||
LzdRrsWweePQjNzuEgjgSUXh0IH0OyoMVGFzayBDcmVhdGVkMAE5aKkrAMegGhhBaCYsAMegGhhK
|
||||
LgoIY3Jld19rZXkSIgogODBjNzk4ZjYyMjhmMzJhNzQ4M2Y3MmFmZTM2NmVkY2FKMQoHY3Jld19p
|
||||
ZBImCiQ0YzM3YTFhNS1lMzA5LTQ2N2EtYWJkZC0zZDY1YThlNjY5ZjBKLgoIdGFza19rZXkSIgog
|
||||
Y2M0YTQyYzE4NmVlMWEyZTY2YjAyOGVjNWI3MmJkNGVKMQoHdGFza19pZBImCiQyZTIyZmIwMy0z
|
||||
MjExLTQ1ODEtOTM3YS1mNjVmOTkyNjdmYjJ6AhgBhQEAAQAAEo4CChDxJ8ZFykKBgfaipCQ/ggPb
|
||||
EgguzV65sDQE1yoMVGFzayBDcmVhdGVkMAE5OBNvAMegGhhBgIRvAMegGhhKLgoIY3Jld19rZXkS
|
||||
IgogODBjNzk4ZjYyMjhmMzJhNzQ4M2Y3MmFmZTM2NmVkY2FKMQoHY3Jld19pZBImCiQ0YzM3YTFh
|
||||
NS1lMzA5LTQ2N2EtYWJkZC0zZDY1YThlNjY5ZjBKLgoIdGFza19rZXkSIgogNzRlNmIyNDQ5YzQ1
|
||||
NzRhY2JjMmJmNDk3MjczYTVjYzFKMQoHdGFza19pZBImCiQ4MjNiZGVjNS0xNGQxLTRkN2MtYmRh
|
||||
Yy04OTM1NjViYWJiYzV6AhgBhQEAAQAAEo4CChC0QeqqmE8Dp/Ee9DEhuLMuEggOnt12q4mouioM
|
||||
VGFzayBDcmVhdGVkMAE5eBbHAMegGhhB2IPHAMegGhhKLgoIY3Jld19rZXkSIgogODBjNzk4ZjYy
|
||||
MjhmMzJhNzQ4M2Y3MmFmZTM2NmVkY2FKMQoHY3Jld19pZBImCiQ0YzM3YTFhNS1lMzA5LTQ2N2Et
|
||||
YWJkZC0zZDY1YThlNjY5ZjBKLgoIdGFza19rZXkSIgogNzRlNmIyNDQ5YzQ1NzRhY2JjMmJmNDk3
|
||||
MjczYTVjYzFKMQoHdGFza19pZBImCiQ4MjNiZGVjNS0xNGQxLTRkN2MtYmRhYy04OTM1NjViYWJi
|
||||
YzV6AhgBhQEAAQAAEsoLChAQHimti07LsJEmR4M5P2iQEgjeCnwCLR02XyoMQ3JldyBDcmVhdGVk
|
||||
MAE5IOlAAsegGhhBAGVJAsegGhhKGgoOY3Jld2FpX3ZlcnNpb24SCAoGMC45NS4wShoKDnB5dGhv
|
||||
bl92ZXJzaW9uEggKBjMuMTIuN0ouCghjcmV3X2tleRIiCiBhYzdlNzQ1OTA3MmM3ZWMwNmRlYWY5
|
||||
ZDMyZWNlYzE1YUoxCgdjcmV3X2lkEiYKJGI1NTdkNDliLTkxZTktNDllMy1iNjA4LTUyZTdiMGE1
|
||||
YzZjM0ocCgxjcmV3X3Byb2Nlc3MSDAoKc2VxdWVudGlhbEoRCgtjcmV3X21lbW9yeRICEABKGgoU
|
||||
Y3Jld19udW1iZXJfb2ZfdGFza3MSAhgCShsKFWNyZXdfbnVtYmVyX29mX2FnZW50cxICGAJKiAUK
|
||||
C2NyZXdfYWdlbnRzEvgECvUEW3sia2V5IjogIjhiZDIxMzliNTk3NTE4MTUwNmU0MWZkOWM0NTYz
|
||||
ZDc1IiwgImlkIjogIjM2OTZjN2Q5LTY3MmEtNDZiMy1iZTBjLTMzZjYyNmIxMDBlNyIsICJyb2xl
|
||||
IjogIlJlc2VhcmNoZXIiLCAidmVyYm9zZT8iOiBmYWxzZSwgIm1heF9pdGVyIjogMjAsICJtYXhf
|
||||
cnBtIjogbnVsbCwgImZ1bmN0aW9uX2NhbGxpbmdfbGxtIjogIiIsICJsbG0iOiAiZ3B0LTRvIiwg
|
||||
ImRlbGVnYXRpb25fZW5hYmxlZD8iOiBmYWxzZSwgImFsbG93X2NvZGVfZXhlY3V0aW9uPyI6IGZh
|
||||
bHNlLCAibWF4X3JldHJ5X2xpbWl0IjogMiwgInRvb2xzX25hbWVzIjogW119LCB7ImtleSI6ICI5
|
||||
YTUwMTVlZjQ4OTVkYzYyNzhkNTQ4MThiYTQ0NmFmNyIsICJpZCI6ICJhOTk0ZTY2ZS1hOTkxLTQ0
|
||||
YTYtODkyMS1hODhkNDNkMjY2YmMiLCAicm9sZSI6ICJTZW5pb3IgV3JpdGVyIiwgInZlcmJvc2U/
|
||||
IjogZmFsc2UsICJtYXhfaXRlciI6IDIwLCAibWF4X3JwbSI6IG51bGwsICJmdW5jdGlvbl9jYWxs
|
||||
aW5nX2xsbSI6ICIiLCAibGxtIjogImdwdC00byIsICJkZWxlZ2F0aW9uX2VuYWJsZWQ/IjogZmFs
|
||||
c2UsICJhbGxvd19jb2RlX2V4ZWN1dGlvbj8iOiBmYWxzZSwgIm1heF9yZXRyeV9saW1pdCI6IDIs
|
||||
ICJ0b29sc19uYW1lcyI6IFtdfV1K7wMKCmNyZXdfdGFza3MS4AMK3QNbeyJrZXkiOiAiYTgwNjE3
|
||||
MTcyZmZjYjkwZjg5N2MxYThjMzJjMzEwMmEiLCAiaWQiOiAiZjNmMDYxNWItMDg3NS00NWM0LWFm
|
||||
YmMtYWI1OGQxMGQyZDA0IiwgImFzeW5jX2V4ZWN1dGlvbj8iOiBmYWxzZSwgImh1bWFuX2lucHV0
|
||||
PyI6IGZhbHNlLCAiYWdlbnRfcm9sZSI6ICJSZXNlYXJjaGVyIiwgImFnZW50X2tleSI6ICI4YmQy
|
||||
MTM5YjU5NzUxODE1MDZlNDFmZDljNDU2M2Q3NSIsICJ0b29sc19uYW1lcyI6IFtdfSwgeyJrZXki
|
||||
OiAiNWZhNjVjMDZhOWUzMWYyYzY5NTQzMjY2OGFjZDYyZGQiLCAiaWQiOiAiNGUwZTEyOTQtZjdi
|
||||
ZS00OTBhLThiYmUtNjliYjQ5ODc1YTUzIiwgImFzeW5jX2V4ZWN1dGlvbj8iOiBmYWxzZSwgImh1
|
||||
bWFuX2lucHV0PyI6IGZhbHNlLCAiYWdlbnRfcm9sZSI6ICJTZW5pb3IgV3JpdGVyIiwgImFnZW50
|
||||
X2tleSI6ICI5YTUwMTVlZjQ4OTVkYzYyNzhkNTQ4MThiYTQ0NmFmNyIsICJ0b29sc19uYW1lcyI6
|
||||
IFtdfV16AhgBhQEAAQAAEo4CChBu6pl3tRo8XQcOz1dOfEiREgi+aKvpuUNN/ioMVGFzayBDcmVh
|
||||
dGVkMAE5QCRZAsegGhhBKKVZAsegGhhKLgoIY3Jld19rZXkSIgogYWM3ZTc0NTkwNzJjN2VjMDZk
|
||||
ZWFmOWQzMmVjZWMxNWFKMQoHY3Jld19pZBImCiRiNTU3ZDQ5Yi05MWU5LTQ5ZTMtYjYwOC01MmU3
|
||||
YjBhNWM2YzNKLgoIdGFza19rZXkSIgogYTgwNjE3MTcyZmZjYjkwZjg5N2MxYThjMzJjMzEwMmFK
|
||||
MQoHdGFza19pZBImCiRmM2YwNjE1Yi0wODc1LTQ1YzQtYWZiYy1hYjU4ZDEwZDJkMDR6AhgBhQEA
|
||||
AQAAEo4CChBNL9q8o7PtXvaR6poXIlx6EggIBAybRwvpyCoMVGFzayBDcmVhdGVkMAE5qP2oAseg
|
||||
GhhB6JmpAsegGhhKLgoIY3Jld19rZXkSIgogYWM3ZTc0NTkwNzJjN2VjMDZkZWFmOWQzMmVjZWMx
|
||||
NWFKMQoHY3Jld19pZBImCiRiNTU3ZDQ5Yi05MWU5LTQ5ZTMtYjYwOC01MmU3YjBhNWM2YzNKLgoI
|
||||
dGFza19rZXkSIgogNWZhNjVjMDZhOWUzMWYyYzY5NTQzMjY2OGFjZDYyZGRKMQoHdGFza19pZBIm
|
||||
CiQ0ZTBlMTI5NC1mN2JlLTQ5MGEtOGJiZS02OWJiNDk4NzVhNTN6AhgBhQEAAQAAEsoLChAxUBRb
|
||||
Q0xWxbf9ef52QMDSEgihBkurLl3qiSoMQ3JldyBDcmVhdGVkMAE5eE9hBcegGhhBCIVpBcegGhhK
|
||||
GgoOY3Jld2FpX3ZlcnNpb24SCAoGMC45NS4wShoKDnB5dGhvbl92ZXJzaW9uEggKBjMuMTIuN0ou
|
||||
CghjcmV3X2tleRIiCiBhYzdlNzQ1OTA3MmM3ZWMwNmRlYWY5ZDMyZWNlYzE1YUoxCgdjcmV3X2lk
|
||||
EiYKJGU1YmYwYTFjLTg2YjctNDhkZC04YzJlLTdjMThhZTZhODJhZUocCgxjcmV3X3Byb2Nlc3MS
|
||||
DAoKc2VxdWVudGlhbEoRCgtjcmV3X21lbW9yeRICEABKGgoUY3Jld19udW1iZXJfb2ZfdGFza3MS
|
||||
AhgCShsKFWNyZXdfbnVtYmVyX29mX2FnZW50cxICGAJKiAUKC2NyZXdfYWdlbnRzEvgECvUEW3si
|
||||
a2V5IjogIjhiZDIxMzliNTk3NTE4MTUwNmU0MWZkOWM0NTYzZDc1IiwgImlkIjogIjM2OTZjN2Q5
|
||||
LTY3MmEtNDZiMy1iZTBjLTMzZjYyNmIxMDBlNyIsICJyb2xlIjogIlJlc2VhcmNoZXIiLCAidmVy
|
||||
Ym9zZT8iOiBmYWxzZSwgIm1heF9pdGVyIjogMjAsICJtYXhfcnBtIjogbnVsbCwgImZ1bmN0aW9u
|
||||
X2NhbGxpbmdfbGxtIjogIiIsICJsbG0iOiAiZ3B0LTRvIiwgImRlbGVnYXRpb25fZW5hYmxlZD8i
|
||||
OiBmYWxzZSwgImFsbG93X2NvZGVfZXhlY3V0aW9uPyI6IGZhbHNlLCAibWF4X3JldHJ5X2xpbWl0
|
||||
IjogMiwgInRvb2xzX25hbWVzIjogW119LCB7ImtleSI6ICI5YTUwMTVlZjQ4OTVkYzYyNzhkNTQ4
|
||||
MThiYTQ0NmFmNyIsICJpZCI6ICJhOTk0ZTY2ZS1hOTkxLTQ0YTYtODkyMS1hODhkNDNkMjY2YmMi
|
||||
LCAicm9sZSI6ICJTZW5pb3IgV3JpdGVyIiwgInZlcmJvc2U/IjogZmFsc2UsICJtYXhfaXRlciI6
|
||||
IDIwLCAibWF4X3JwbSI6IG51bGwsICJmdW5jdGlvbl9jYWxsaW5nX2xsbSI6ICIiLCAibGxtIjog
|
||||
ImdwdC00byIsICJkZWxlZ2F0aW9uX2VuYWJsZWQ/IjogZmFsc2UsICJhbGxvd19jb2RlX2V4ZWN1
|
||||
dGlvbj8iOiBmYWxzZSwgIm1heF9yZXRyeV9saW1pdCI6IDIsICJ0b29sc19uYW1lcyI6IFtdfV1K
|
||||
7wMKCmNyZXdfdGFza3MS4AMK3QNbeyJrZXkiOiAiYTgwNjE3MTcyZmZjYjkwZjg5N2MxYThjMzJj
|
||||
MzEwMmEiLCAiaWQiOiAiMDJlMTk1ODMtZmY3OS00N2YzLThkNDMtNWJhMGY4NmYxOTllIiwgImFz
|
||||
eW5jX2V4ZWN1dGlvbj8iOiBmYWxzZSwgImh1bWFuX2lucHV0PyI6IGZhbHNlLCAiYWdlbnRfcm9s
|
||||
ZSI6ICJSZXNlYXJjaGVyIiwgImFnZW50X2tleSI6ICI4YmQyMTM5YjU5NzUxODE1MDZlNDFmZDlj
|
||||
NDU2M2Q3NSIsICJ0b29sc19uYW1lcyI6IFtdfSwgeyJrZXkiOiAiNWZhNjVjMDZhOWUzMWYyYzY5
|
||||
NTQzMjY2OGFjZDYyZGQiLCAiaWQiOiAiY2ViMjZhOTUtODc5ZS00OGFmLTg2MmItNzAyZmIyODA3
|
||||
MzM5IiwgImFzeW5jX2V4ZWN1dGlvbj8iOiBmYWxzZSwgImh1bWFuX2lucHV0PyI6IGZhbHNlLCAi
|
||||
YWdlbnRfcm9sZSI6ICJTZW5pb3IgV3JpdGVyIiwgImFnZW50X2tleSI6ICI5YTUwMTVlZjQ4OTVk
|
||||
YzYyNzhkNTQ4MThiYTQ0NmFmNyIsICJ0b29sc19uYW1lcyI6IFtdfV16AhgBhQEAAQAAEo4CChD9
|
||||
XNrHzMkqfERO3pxva7qVEgi+KDMFQWeCXioMVGFzayBDcmVhdGVkMAE5KHl4BcegGhhBKPZ4Bceg
|
||||
GhhKLgoIY3Jld19rZXkSIgogYWM3ZTc0NTkwNzJjN2VjMDZkZWFmOWQzMmVjZWMxNWFKMQoHY3Jl
|
||||
d19pZBImCiRlNWJmMGExYy04NmI3LTQ4ZGQtOGMyZS03YzE4YWU2YTgyYWVKLgoIdGFza19rZXkS
|
||||
IgogYTgwNjE3MTcyZmZjYjkwZjg5N2MxYThjMzJjMzEwMmFKMQoHdGFza19pZBImCiQwMmUxOTU4
|
||||
My1mZjc5LTQ3ZjMtOGQ0My01YmEwZjg2ZjE5OWV6AhgBhQEAAQAAEsoLChBy2/tEpjdjZeT9McCa
|
||||
zn1ZEghPIBt/a/+PUyoMQ3JldyBDcmVhdGVkMAE5ABE/BsegGhhB+PlJBsegGhhKGgoOY3Jld2Fp
|
||||
X3ZlcnNpb24SCAoGMC45NS4wShoKDnB5dGhvbl92ZXJzaW9uEggKBjMuMTIuN0ouCghjcmV3X2tl
|
||||
eRIiCiBkMjdkNDVhZDlkYTE1ODU0MzI1YjBhZjNiMGZiYzMyYkoxCgdjcmV3X2lkEiYKJGM4OGMx
|
||||
ZDc1LWZlN2QtNDQwMi04N2QwLWFkYzQ3MWFiMWI3YUocCgxjcmV3X3Byb2Nlc3MSDAoKc2VxdWVu
|
||||
dGlhbEoRCgtjcmV3X21lbW9yeRICEABKGgoUY3Jld19udW1iZXJfb2ZfdGFza3MSAhgCShsKFWNy
|
||||
ZXdfbnVtYmVyX29mX2FnZW50cxICGAJKiAUKC2NyZXdfYWdlbnRzEvgECvUEW3sia2V5IjogIjhi
|
||||
ZDIxMzliNTk3NTE4MTUwNmU0MWZkOWM0NTYzZDc1IiwgImlkIjogIjM2OTZjN2Q5LTY3MmEtNDZi
|
||||
My1iZTBjLTMzZjYyNmIxMDBlNyIsICJyb2xlIjogIlJlc2VhcmNoZXIiLCAidmVyYm9zZT8iOiBm
|
||||
YWxzZSwgIm1heF9pdGVyIjogMjAsICJtYXhfcnBtIjogbnVsbCwgImZ1bmN0aW9uX2NhbGxpbmdf
|
||||
bGxtIjogIiIsICJsbG0iOiAiZ3B0LTRvIiwgImRlbGVnYXRpb25fZW5hYmxlZD8iOiBmYWxzZSwg
|
||||
ImFsbG93X2NvZGVfZXhlY3V0aW9uPyI6IGZhbHNlLCAibWF4X3JldHJ5X2xpbWl0IjogMiwgInRv
|
||||
b2xzX25hbWVzIjogW119LCB7ImtleSI6ICI5YTUwMTVlZjQ4OTVkYzYyNzhkNTQ4MThiYTQ0NmFm
|
||||
NyIsICJpZCI6ICJhOTk0ZTY2ZS1hOTkxLTQ0YTYtODkyMS1hODhkNDNkMjY2YmMiLCAicm9sZSI6
|
||||
ICJTZW5pb3IgV3JpdGVyIiwgInZlcmJvc2U/IjogZmFsc2UsICJtYXhfaXRlciI6IDIwLCAibWF4
|
||||
X3JwbSI6IG51bGwsICJmdW5jdGlvbl9jYWxsaW5nX2xsbSI6ICIiLCAibGxtIjogImdwdC00byIs
|
||||
ICJkZWxlZ2F0aW9uX2VuYWJsZWQ/IjogZmFsc2UsICJhbGxvd19jb2RlX2V4ZWN1dGlvbj8iOiBm
|
||||
YWxzZSwgIm1heF9yZXRyeV9saW1pdCI6IDIsICJ0b29sc19uYW1lcyI6IFtdfV1K7wMKCmNyZXdf
|
||||
dGFza3MS4AMK3QNbeyJrZXkiOiAiODE2ZTllYmM2OWRiNjdjNjhiYjRmM2VhNjVjY2RhNTgiLCAi
|
||||
aWQiOiAiZDM1YjllMjUtODE1MC00ODQ0LWFhMTctYzk0MTRhMDE2NjcyIiwgImFzeW5jX2V4ZWN1
|
||||
dGlvbj8iOiBmYWxzZSwgImh1bWFuX2lucHV0PyI6IGZhbHNlLCAiYWdlbnRfcm9sZSI6ICJSZXNl
|
||||
YXJjaGVyIiwgImFnZW50X2tleSI6ICI4YmQyMTM5YjU5NzUxODE1MDZlNDFmZDljNDU2M2Q3NSIs
|
||||
ICJ0b29sc19uYW1lcyI6IFtdfSwgeyJrZXkiOiAiNWZhNjVjMDZhOWUzMWYyYzY5NTQzMjY2OGFj
|
||||
ZDYyZGQiLCAiaWQiOiAiYjIwMjdlZWUtYjNjYi00MGMxLWI1NDEtNmY0ZTA5ZGRhNTU5IiwgImFz
|
||||
eW5jX2V4ZWN1dGlvbj8iOiBmYWxzZSwgImh1bWFuX2lucHV0PyI6IGZhbHNlLCAiYWdlbnRfcm9s
|
||||
ZSI6ICJTZW5pb3IgV3JpdGVyIiwgImFnZW50X2tleSI6ICI5YTUwMTVlZjQ4OTVkYzYyNzhkNTQ4
|
||||
MThiYTQ0NmFmNyIsICJ0b29sc19uYW1lcyI6IFtdfV16AhgBhQEAAQAAEsoLChD//jBA0L4Z7qgQ
|
||||
5xomV5+TEgjd+k4M+YdqbCoMQ3JldyBDcmVhdGVkMAE5uAq/BsegGhhB6EPJBsegGhhKGgoOY3Jl
|
||||
d2FpX3ZlcnNpb24SCAoGMC45NS4wShoKDnB5dGhvbl92ZXJzaW9uEggKBjMuMTIuN0ouCghjcmV3
|
||||
X2tleRIiCiBkMjdkNDVhZDlkYTE1ODU0MzI1YjBhZjNiMGZiYzMyYkoxCgdjcmV3X2lkEiYKJGY3
|
||||
OTg0ZWVlLWZjMGItNGFjYy1iNWE3LWExYjgwMWU0NGM1MEocCgxjcmV3X3Byb2Nlc3MSDAoKc2Vx
|
||||
dWVudGlhbEoRCgtjcmV3X21lbW9yeRICEABKGgoUY3Jld19udW1iZXJfb2ZfdGFza3MSAhgCShsK
|
||||
FWNyZXdfbnVtYmVyX29mX2FnZW50cxICGAJKiAUKC2NyZXdfYWdlbnRzEvgECvUEW3sia2V5Ijog
|
||||
IjhiZDIxMzliNTk3NTE4MTUwNmU0MWZkOWM0NTYzZDc1IiwgImlkIjogIjM2OTZjN2Q5LTY3MmEt
|
||||
NDZiMy1iZTBjLTMzZjYyNmIxMDBlNyIsICJyb2xlIjogIlJlc2VhcmNoZXIiLCAidmVyYm9zZT8i
|
||||
OiBmYWxzZSwgIm1heF9pdGVyIjogMjAsICJtYXhfcnBtIjogbnVsbCwgImZ1bmN0aW9uX2NhbGxp
|
||||
bmdfbGxtIjogIiIsICJsbG0iOiAiZ3B0LTRvIiwgImRlbGVnYXRpb25fZW5hYmxlZD8iOiBmYWxz
|
||||
ZSwgImFsbG93X2NvZGVfZXhlY3V0aW9uPyI6IGZhbHNlLCAibWF4X3JldHJ5X2xpbWl0IjogMiwg
|
||||
InRvb2xzX25hbWVzIjogW119LCB7ImtleSI6ICI5YTUwMTVlZjQ4OTVkYzYyNzhkNTQ4MThiYTQ0
|
||||
NmFmNyIsICJpZCI6ICJhOTk0ZTY2ZS1hOTkxLTQ0YTYtODkyMS1hODhkNDNkMjY2YmMiLCAicm9s
|
||||
ZSI6ICJTZW5pb3IgV3JpdGVyIiwgInZlcmJvc2U/IjogZmFsc2UsICJtYXhfaXRlciI6IDIwLCAi
|
||||
bWF4X3JwbSI6IG51bGwsICJmdW5jdGlvbl9jYWxsaW5nX2xsbSI6ICIiLCAibGxtIjogImdwdC00
|
||||
byIsICJkZWxlZ2F0aW9uX2VuYWJsZWQ/IjogZmFsc2UsICJhbGxvd19jb2RlX2V4ZWN1dGlvbj8i
|
||||
OiBmYWxzZSwgIm1heF9yZXRyeV9saW1pdCI6IDIsICJ0b29sc19uYW1lcyI6IFtdfV1K7wMKCmNy
|
||||
ZXdfdGFza3MS4AMK3QNbeyJrZXkiOiAiODE2ZTllYmM2OWRiNjdjNjhiYjRmM2VhNjVjY2RhNTgi
|
||||
LCAiaWQiOiAiOTcxMDdmNTUtY2U2Yi00NWI4LWI4Y2QtZjhjNmIyOGI1YjI5IiwgImFzeW5jX2V4
|
||||
ZWN1dGlvbj8iOiBmYWxzZSwgImh1bWFuX2lucHV0PyI6IGZhbHNlLCAiYWdlbnRfcm9sZSI6ICJS
|
||||
ZXNlYXJjaGVyIiwgImFnZW50X2tleSI6ICI4YmQyMTM5YjU5NzUxODE1MDZlNDFmZDljNDU2M2Q3
|
||||
NSIsICJ0b29sc19uYW1lcyI6IFtdfSwgeyJrZXkiOiAiNWZhNjVjMDZhOWUzMWYyYzY5NTQzMjY2
|
||||
OGFjZDYyZGQiLCAiaWQiOiAiNzZlMTYxMDEtNTY3ZC00YmVlLTg3MGQtNjlkNjUzNWUxM2Y0Iiwg
|
||||
ImFzeW5jX2V4ZWN1dGlvbj8iOiBmYWxzZSwgImh1bWFuX2lucHV0PyI6IGZhbHNlLCAiYWdlbnRf
|
||||
cm9sZSI6ICJTZW5pb3IgV3JpdGVyIiwgImFnZW50X2tleSI6ICI5YTUwMTVlZjQ4OTVkYzYyNzhk
|
||||
NTQ4MThiYTQ0NmFmNyIsICJ0b29sc19uYW1lcyI6IFtdfV16AhgBhQEAAQAAEv4BChBUyY/ccsE1
|
||||
R24CGyVtHLqZEgiwrBqbcxAHeCoTQ3JldyBUZXN0IEV4ZWN1dGlvbjABOSiyJAfHoBoYQZiNLgfH
|
||||
oBoYShoKDmNyZXdhaV92ZXJzaW9uEggKBjAuOTUuMEouCghjcmV3X2tleRIiCiAzOTQ5M2UxNjE2
|
||||
MzRhOWVjNGRjNGUzOTdhOTc2OTU3MkoxCgdjcmV3X2lkEiYKJGUwZWJlYWE2LTFjMmItNGMxZi1i
|
||||
MzY1LTE4YmNmMjZhOGIwNkoRCgppdGVyYXRpb25zEgMKATJKGwoKbW9kZWxfbmFtZRINCgtncHQt
|
||||
NG8tbWluaXoCGAGFAQABAAASuAkKEPPNALYHa18lwaRtQDvBnDESCJJZx6P/4qPDKgxDcmV3IENy
|
||||
ZWF0ZWQwATnIzZ8Hx6AaGEFIWagHx6AaGEoaCg5jcmV3YWlfdmVyc2lvbhIICgYwLjk1LjBKGgoO
|
||||
cHl0aG9uX3ZlcnNpb24SCAoGMy4xMi43Si4KCGNyZXdfa2V5EiIKIGUzZmRhMGYzMTEwZmU4MGIx
|
||||
ODk0N2MwMTQ3MTQzMGE0SjEKB2NyZXdfaWQSJgokMTBhYzc4ODQtOTA2ZC00YTg0LWIxMTYtMWMx
|
||||
MTg5NDg3OTc3Sh4KDGNyZXdfcHJvY2VzcxIOCgxoaWVyYXJjaGljYWxKEQoLY3Jld19tZW1vcnkS
|
||||
AhAAShoKFGNyZXdfbnVtYmVyX29mX3Rhc2tzEgIYAUobChVjcmV3X251bWJlcl9vZl9hZ2VudHMS
|
||||
AhgCSogFCgtjcmV3X2FnZW50cxL4BAr1BFt7ImtleSI6ICI4YmQyMTM5YjU5NzUxODE1MDZlNDFm
|
||||
ZDljNDU2M2Q3NSIsICJpZCI6ICIzNjk2YzdkOS02NzJhLTQ2YjMtYmUwYy0zM2Y2MjZiMTAwZTci
|
||||
LCAicm9sZSI6ICJSZXNlYXJjaGVyIiwgInZlcmJvc2U/IjogZmFsc2UsICJtYXhfaXRlciI6IDIw
|
||||
LCAibWF4X3JwbSI6IG51bGwsICJmdW5jdGlvbl9jYWxsaW5nX2xsbSI6ICIiLCAibGxtIjogImdw
|
||||
dC00byIsICJkZWxlZ2F0aW9uX2VuYWJsZWQ/IjogZmFsc2UsICJhbGxvd19jb2RlX2V4ZWN1dGlv
|
||||
bj8iOiBmYWxzZSwgIm1heF9yZXRyeV9saW1pdCI6IDIsICJ0b29sc19uYW1lcyI6IFtdfSwgeyJr
|
||||
ZXkiOiAiOWE1MDE1ZWY0ODk1ZGM2Mjc4ZDU0ODE4YmE0NDZhZjciLCAiaWQiOiAiYTk5NGU2NmUt
|
||||
YTk5MS00NGE2LTg5MjEtYTg4ZDQzZDI2NmJjIiwgInJvbGUiOiAiU2VuaW9yIFdyaXRlciIsICJ2
|
||||
ZXJib3NlPyI6IGZhbHNlLCAibWF4X2l0ZXIiOiAyMCwgIm1heF9ycG0iOiBudWxsLCAiZnVuY3Rp
|
||||
b25fY2FsbGluZ19sbG0iOiAiIiwgImxsbSI6ICJncHQtNG8iLCAiZGVsZWdhdGlvbl9lbmFibGVk
|
||||
PyI6IGZhbHNlLCAiYWxsb3dfY29kZV9leGVjdXRpb24/IjogZmFsc2UsICJtYXhfcmV0cnlfbGlt
|
||||
aXQiOiAyLCAidG9vbHNfbmFtZXMiOiBbXX1dStsBCgpjcmV3X3Rhc2tzEswBCskBW3sia2V5Ijog
|
||||
IjVmYTY1YzA2YTllMzFmMmM2OTU0MzI2NjhhY2Q2MmRkIiwgImlkIjogIjYzYmEzZTVmLWNlOWIt
|
||||
NDE4Zi04NGNmLWJjOWNlYjUwYTMwNyIsICJhc3luY19leGVjdXRpb24/IjogZmFsc2UsICJodW1h
|
||||
bl9pbnB1dD8iOiBmYWxzZSwgImFnZW50X3JvbGUiOiAiTm9uZSIsICJhZ2VudF9rZXkiOiBudWxs
|
||||
LCAidG9vbHNfbmFtZXMiOiBbXX1degIYAYUBAAEAABKOAgoQlnr9jeEDn0IZusmEkE/xBxIIbyk0
|
||||
sNkOWxwqDFRhc2sgQ3JlYXRlZDABOdAdygfHoBoYQQCTygfHoBoYSi4KCGNyZXdfa2V5EiIKIGUz
|
||||
ZmRhMGYzMTEwZmU4MGIxODk0N2MwMTQ3MTQzMGE0SjEKB2NyZXdfaWQSJgokMTBhYzc4ODQtOTA2
|
||||
ZC00YTg0LWIxMTYtMWMxMTg5NDg3OTc3Si4KCHRhc2tfa2V5EiIKIDVmYTY1YzA2YTllMzFmMmM2
|
||||
OTU0MzI2NjhhY2Q2MmRkSjEKB3Rhc2tfaWQSJgokNjNiYTNlNWYtY2U5Yi00MThmLTg0Y2YtYmM5
|
||||
Y2ViNTBhMzA3egIYAYUBAAEAABKcAQoQbJPP7Nx3r3ewgPHdeJybDBIIlUb3D4pi3dkqClRvb2wg
|
||||
VXNhZ2UwATmonCAKx6AaGEEgUykKx6AaGEoaCg5jcmV3YWlfdmVyc2lvbhIICgYwLjk1LjBKKAoJ
|
||||
dG9vbF9uYW1lEhsKGURlbGVnYXRlIHdvcmsgdG8gY293b3JrZXJKDgoIYXR0ZW1wdHMSAhgBegIY
|
||||
AYUBAAEAABKcAQoQ1SSOOcoVWGrQIs6azsmxmBIIGSOj86a7GPsqClRvb2wgVXNhZ2UwATmA8e4O
|
||||
x6AaGEGo3vcOx6AaGEoaCg5jcmV3YWlfdmVyc2lvbhIICgYwLjk1LjBKKAoJdG9vbF9uYW1lEhsK
|
||||
GURlbGVnYXRlIHdvcmsgdG8gY293b3JrZXJKDgoIYXR0ZW1wdHMSAhgBegIYAYUBAAEAABK4CQoQ
|
||||
EQHO/mvzkyYWgZwwn+Rc5BIIv4Hy3+pCFpYqDENyZXcgQ3JlYXRlZDABOTgFvg/HoBoYQfi1xQ/H
|
||||
oBoYShoKDmNyZXdhaV92ZXJzaW9uEggKBjAuOTUuMEoaCg5weXRob25fdmVyc2lvbhIICgYzLjEy
|
||||
LjdKLgoIY3Jld19rZXkSIgogZTNmZGEwZjMxMTBmZTgwYjE4OTQ3YzAxNDcxNDMwYTRKMQoHY3Jl
|
||||
d19pZBImCiQxYTNiYWYyMi04ZDA3LTRiOTctOGM4Ni1kMmM0NDNlYTZkZjdKHgoMY3Jld19wcm9j
|
||||
ZXNzEg4KDGhpZXJhcmNoaWNhbEoRCgtjcmV3X21lbW9yeRICEABKGgoUY3Jld19udW1iZXJfb2Zf
|
||||
dGFza3MSAhgBShsKFWNyZXdfbnVtYmVyX29mX2FnZW50cxICGAJKiAUKC2NyZXdfYWdlbnRzEvgE
|
||||
CvUEW3sia2V5IjogIjhiZDIxMzliNTk3NTE4MTUwNmU0MWZkOWM0NTYzZDc1IiwgImlkIjogIjM2
|
||||
OTZjN2Q5LTY3MmEtNDZiMy1iZTBjLTMzZjYyNmIxMDBlNyIsICJyb2xlIjogIlJlc2VhcmNoZXIi
|
||||
LCAidmVyYm9zZT8iOiBmYWxzZSwgIm1heF9pdGVyIjogMjAsICJtYXhfcnBtIjogbnVsbCwgImZ1
|
||||
bmN0aW9uX2NhbGxpbmdfbGxtIjogIiIsICJsbG0iOiAiZ3B0LTRvIiwgImRlbGVnYXRpb25fZW5h
|
||||
YmxlZD8iOiBmYWxzZSwgImFsbG93X2NvZGVfZXhlY3V0aW9uPyI6IGZhbHNlLCAibWF4X3JldHJ5
|
||||
X2xpbWl0IjogMiwgInRvb2xzX25hbWVzIjogW119LCB7ImtleSI6ICI5YTUwMTVlZjQ4OTVkYzYy
|
||||
NzhkNTQ4MThiYTQ0NmFmNyIsICJpZCI6ICJhOTk0ZTY2ZS1hOTkxLTQ0YTYtODkyMS1hODhkNDNk
|
||||
MjY2YmMiLCAicm9sZSI6ICJTZW5pb3IgV3JpdGVyIiwgInZlcmJvc2U/IjogZmFsc2UsICJtYXhf
|
||||
aXRlciI6IDIwLCAibWF4X3JwbSI6IG51bGwsICJmdW5jdGlvbl9jYWxsaW5nX2xsbSI6ICIiLCAi
|
||||
bGxtIjogImdwdC00byIsICJkZWxlZ2F0aW9uX2VuYWJsZWQ/IjogZmFsc2UsICJhbGxvd19jb2Rl
|
||||
X2V4ZWN1dGlvbj8iOiBmYWxzZSwgIm1heF9yZXRyeV9saW1pdCI6IDIsICJ0b29sc19uYW1lcyI6
|
||||
IFtdfV1K2wEKCmNyZXdfdGFza3MSzAEKyQFbeyJrZXkiOiAiNWZhNjVjMDZhOWUzMWYyYzY5NTQz
|
||||
MjY2OGFjZDYyZGQiLCAiaWQiOiAiZWYxYjNhN2MtOTMxYi00MjRjLTkxMzQtZDY1OTM1N2I3ODNi
|
||||
IiwgImFzeW5jX2V4ZWN1dGlvbj8iOiBmYWxzZSwgImh1bWFuX2lucHV0PyI6IGZhbHNlLCAiYWdl
|
||||
bnRfcm9sZSI6ICJOb25lIiwgImFnZW50X2tleSI6IG51bGwsICJ0b29sc19uYW1lcyI6IFtdfV16
|
||||
AhgBhQEAAQAAEo4CChBZkLAu5xnAQh/ILJnU7h1REggAGIt5Pa4D3ioMVGFzayBDcmVhdGVkMAE5
|
||||
AMXlD8egGhhBwCLmD8egGhhKLgoIY3Jld19rZXkSIgogZTNmZGEwZjMxMTBmZTgwYjE4OTQ3YzAx
|
||||
NDcxNDMwYTRKMQoHY3Jld19pZBImCiQxYTNiYWYyMi04ZDA3LTRiOTctOGM4Ni1kMmM0NDNlYTZk
|
||||
ZjdKLgoIdGFza19rZXkSIgogNWZhNjVjMDZhOWUzMWYyYzY5NTQzMjY2OGFjZDYyZGRKMQoHdGFz
|
||||
a19pZBImCiRlZjFiM2E3Yy05MzFiLTQyNGMtOTEzNC1kNjU5MzU3Yjc4M2J6AhgBhQEAAQAAEpwB
|
||||
ChBl/QzggjWFEfDigYrgsKMhEgjIhVTOpOyNnioKVG9vbCBVc2FnZTABOWi8pxHHoBoYQYhdrxHH
|
||||
oBoYShoKDmNyZXdhaV92ZXJzaW9uEggKBjAuOTUuMEooCgl0b29sX25hbWUSGwoZRGVsZWdhdGUg
|
||||
d29yayB0byBjb3dvcmtlckoOCghhdHRlbXB0cxICGAF6AhgBhQEAAQAAEpwBChC1Cxzix7ErLK5V
|
||||
rNWRMj7jEgjEMld4I2kVXCoKVG9vbCBVc2FnZTABOSh2whjHoBoYQSi9yxjHoBoYShoKDmNyZXdh
|
||||
aV92ZXJzaW9uEggKBjAuOTUuMEooCgl0b29sX25hbWUSGwoZRGVsZWdhdGUgd29yayB0byBjb3dv
|
||||
cmtlckoOCghhdHRlbXB0cxICGAF6AhgBhQEAAQAAEuEJChCh/OOje68hh/B1dkfbmjf/Egje+GUm
|
||||
CUGqZCoMQ3JldyBDcmVhdGVkMAE5cBtkV8egGhhBcD5zV8egGhhKGgoOY3Jld2FpX3ZlcnNpb24S
|
||||
CAoGMC45NS4wShoKDnB5dGhvbl92ZXJzaW9uEggKBjMuMTIuN0ouCghjcmV3X2tleRIiCiBjYWEx
|
||||
YWViM2RkNDM2Mzg2NTY4YTVjM2ZlMjEwMWFmNUoxCgdjcmV3X2lkEiYKJDdlZWUxNTA4LWQwNGIt
|
||||
NDczYy1iZjhmLTJkODgxNGU1MjNhN0ocCgxjcmV3X3Byb2Nlc3MSDAoKc2VxdWVudGlhbEoRCgtj
|
||||
cmV3X21lbW9yeRICEABKGgoUY3Jld19udW1iZXJfb2ZfdGFza3MSAhgBShsKFWNyZXdfbnVtYmVy
|
||||
X29mX2FnZW50cxICGAJKhAUKC2NyZXdfYWdlbnRzEvQECvEEW3sia2V5IjogIjk3ZjQxN2YzZTFl
|
||||
MzFjZjBjMTA5Zjc1MjlhYzhmNmJjIiwgImlkIjogIjQwM2ZkM2Q2LTAxNTYtNDIwMS04OGFmLTU0
|
||||
MjU5YjczNzJkYSIsICJyb2xlIjogIlByb2dyYW1tZXIiLCAidmVyYm9zZT8iOiBmYWxzZSwgIm1h
|
||||
eF9pdGVyIjogMjAsICJtYXhfcnBtIjogbnVsbCwgImZ1bmN0aW9uX2NhbGxpbmdfbGxtIjogIiIs
|
||||
ICJsbG0iOiAiZ3B0LTRvIiwgImRlbGVnYXRpb25fZW5hYmxlZD8iOiB0cnVlLCAiYWxsb3dfY29k
|
||||
ZV9leGVjdXRpb24/IjogdHJ1ZSwgIm1heF9yZXRyeV9saW1pdCI6IDIsICJ0b29sc19uYW1lcyI6
|
||||
IFtdfSwgeyJrZXkiOiAiOTJhMjRiMGJjY2ZiMGRjMGU0MzlkN2Q1OWJhOWY2ZjMiLCAiaWQiOiAi
|
||||
YzIxMTQ4ZmQtOGU3NS00NDlhLTg2MmMtNWRiNjQ5Yzc0OTYzIiwgInJvbGUiOiAiQ29kZSBSZXZp
|
||||
ZXdlciIsICJ2ZXJib3NlPyI6IGZhbHNlLCAibWF4X2l0ZXIiOiAyMCwgIm1heF9ycG0iOiBudWxs
|
||||
LCAiZnVuY3Rpb25fY2FsbGluZ19sbG0iOiAiIiwgImxsbSI6ICJncHQtNG8iLCAiZGVsZWdhdGlv
|
||||
bl9lbmFibGVkPyI6IHRydWUsICJhbGxvd19jb2RlX2V4ZWN1dGlvbj8iOiB0cnVlLCAibWF4X3Jl
|
||||
dHJ5X2xpbWl0IjogMiwgInRvb2xzX25hbWVzIjogW119XUqKAgoKY3Jld190YXNrcxL7AQr4AVt7
|
||||
ImtleSI6ICI3OWFhMjdkZjc0ZTYyNzllMzRhODg4ODE3NDgxYzQwZiIsICJpZCI6ICI0ZWYzZWEy
|
||||
OS0xMzNjLTQxNjktODgyMS1jZDI4ZTgxMTYxYmIiLCAiYXN5bmNfZXhlY3V0aW9uPyI6IGZhbHNl
|
||||
LCAiaHVtYW5faW5wdXQ/IjogZmFsc2UsICJhZ2VudF9yb2xlIjogIlByb2dyYW1tZXIiLCAiYWdl
|
||||
bnRfa2V5IjogIjk3ZjQxN2YzZTFlMzFjZjBjMTA5Zjc1MjlhYzhmNmJjIiwgInRvb2xzX25hbWVz
|
||||
IjogWyJ0ZXN0IHRvb2wiXX1degIYAYUBAAEAABKuBwoQjpMoNMb5Vz8kFm796AmokxIIPavlOS8Y
|
||||
ZJ0qDENyZXcgQ3JlYXRlZDABOZg1IVjHoBoYQXBfKVjHoBoYShoKDmNyZXdhaV92ZXJzaW9uEggK
|
||||
BjAuOTUuMEoaCg5weXRob25fdmVyc2lvbhIICgYzLjEyLjdKLgoIY3Jld19rZXkSIgogNzczYTg3
|
||||
NmI1NzkyZGI2OTU1OWZlODJjM2FkMjM1OWZKMQoHY3Jld19pZBImCiQwNDQzNzU1MS0yN2RmLTQ3
|
||||
YTQtOTliNS1iOWNkYmYxMDFhNjZKHAoMY3Jld19wcm9jZXNzEgwKCnNlcXVlbnRpYWxKEQoLY3Jl
|
||||
d19tZW1vcnkSAhAAShoKFGNyZXdfbnVtYmVyX29mX3Rhc2tzEgIYAUobChVjcmV3X251bWJlcl9v
|
||||
Zl9hZ2VudHMSAhgBStQCCgtjcmV3X2FnZW50cxLEAgrBAlt7ImtleSI6ICIwNzdjN2E4NjdlMjBk
|
||||
MGE2OGI5NzRlNDc2MDcxMDlmMyIsICJpZCI6ICIzMDMzZmZkYy03YjI0LTRmMDgtYmNmZS1iYzQz
|
||||
NzhkM2U5NjAiLCAicm9sZSI6ICJNdWx0aW1vZGFsIEFuYWx5c3QiLCAidmVyYm9zZT8iOiBmYWxz
|
||||
ZSwgIm1heF9pdGVyIjogMjAsICJtYXhfcnBtIjogbnVsbCwgImZ1bmN0aW9uX2NhbGxpbmdfbGxt
|
||||
IjogIiIsICJsbG0iOiAiZ3B0LTRvIiwgImRlbGVnYXRpb25fZW5hYmxlZD8iOiBmYWxzZSwgImFs
|
||||
bG93X2NvZGVfZXhlY3V0aW9uPyI6IGZhbHNlLCAibWF4X3JldHJ5X2xpbWl0IjogMiwgInRvb2xz
|
||||
X25hbWVzIjogW119XUqHAgoKY3Jld190YXNrcxL4AQr1AVt7ImtleSI6ICJjNzUzYzY4MDYzNTk0
|
||||
MzZhNTg5NmZlYzA5YmFhMTI1ZSIsICJpZCI6ICI3Y2YxYTRkNC0xMmRjLTRjOWUtOWY1Ny0xZjhk
|
||||
MTc5YmNlZGEiLCAiYXN5bmNfZXhlY3V0aW9uPyI6IGZhbHNlLCAiaHVtYW5faW5wdXQ/IjogZmFs
|
||||
c2UsICJhZ2VudF9yb2xlIjogIk11bHRpbW9kYWwgQW5hbHlzdCIsICJhZ2VudF9rZXkiOiAiMDc3
|
||||
YzdhODY3ZTIwZDBhNjhiOTc0ZTQ3NjA3MTA5ZjMiLCAidG9vbHNfbmFtZXMiOiBbXX1degIYAYUB
|
||||
AAEAABKkBwoQ7zp57STyOlOLCoDVAFh15hIInYYk7J+gZ94qDENyZXcgQ3JlYXRlZDABOYjOfljH
|
||||
oBoYQZhIhljHoBoYShoKDmNyZXdhaV92ZXJzaW9uEggKBjAuOTUuMEoaCg5weXRob25fdmVyc2lv
|
||||
bhIICgYzLjEyLjdKLgoIY3Jld19rZXkSIgogY2Q0ZGE2NGU2ZGMzYjllYmRjYTI0NDRjMWQ3MzAy
|
||||
ODFKMQoHY3Jld19pZBImCiQ1OTlmMjViNS0xMTgzLTQ2OTctODNjMy03OWUzZmQ3MmQ0NDlKHAoM
|
||||
Y3Jld19wcm9jZXNzEgwKCnNlcXVlbnRpYWxKEQoLY3Jld19tZW1vcnkSAhAAShoKFGNyZXdfbnVt
|
||||
YmVyX29mX3Rhc2tzEgIYAUobChVjcmV3X251bWJlcl9vZl9hZ2VudHMSAhgBSs8CCgtjcmV3X2Fn
|
||||
ZW50cxK/Agq8Alt7ImtleSI6ICJkODUxMDY0YjliNDg0MThhYzI1ZjhkMzdjN2UzMmJiNiIsICJp
|
||||
ZCI6ICJiY2I5ZjA4Ny1iMzI2LTRmYTQtOWJlZS0wMGVjODlmZTEwMzEiLCAicm9sZSI6ICJJbWFn
|
||||
ZSBBbmFseXN0IiwgInZlcmJvc2U/IjogZmFsc2UsICJtYXhfaXRlciI6IDIwLCAibWF4X3JwbSI6
|
||||
IG51bGwsICJmdW5jdGlvbl9jYWxsaW5nX2xsbSI6ICIiLCAibGxtIjogImdwdC00byIsICJkZWxl
|
||||
Z2F0aW9uX2VuYWJsZWQ/IjogZmFsc2UsICJhbGxvd19jb2RlX2V4ZWN1dGlvbj8iOiBmYWxzZSwg
|
||||
Im1heF9yZXRyeV9saW1pdCI6IDIsICJ0b29sc19uYW1lcyI6IFtdfV1KggIKCmNyZXdfdGFza3MS
|
||||
8wEK8AFbeyJrZXkiOiAiZWU4NzI5Njk0MTBjOTRjMzM0ZjljZmZhMGE0MTVmZWMiLCAiaWQiOiAi
|
||||
NmFlMDcxYmItMjU4ZS00ZWRkLThhOGItODIxNzU4ZTFhNmRkIiwgImFzeW5jX2V4ZWN1dGlvbj8i
|
||||
OiBmYWxzZSwgImh1bWFuX2lucHV0PyI6IGZhbHNlLCAiYWdlbnRfcm9sZSI6ICJJbWFnZSBBbmFs
|
||||
eXN0IiwgImFnZW50X2tleSI6ICJkODUxMDY0YjliNDg0MThhYzI1ZjhkMzdjN2UzMmJiNiIsICJ0
|
||||
b29sc19uYW1lcyI6IFtdfV16AhgBhQEAAQAAEqMHChBetHqqjbX/OlqTuIZkVppxEgirl8FuUewu
|
||||
TSoMQ3JldyBDcmVhdGVkMAE5aGwoWcegGhhBOCw0WcegGhhKGgoOY3Jld2FpX3ZlcnNpb24SCAoG
|
||||
MC45NS4wShoKDnB5dGhvbl92ZXJzaW9uEggKBjMuMTIuN0ouCghjcmV3X2tleRIiCiBlMzk1Njdi
|
||||
NTA1MjkwOWNhMzM0MDk4NGI4Mzg5ODBlYUoxCgdjcmV3X2lkEiYKJDA2ZTljN2FjLTEzZDItNGU4
|
||||
MS1hNzI2LTBlYjIyYzdlNWQ3MEocCgxjcmV3X3Byb2Nlc3MSDAoKc2VxdWVudGlhbEoRCgtjcmV3
|
||||
X21lbW9yeRICEABKGgoUY3Jld19udW1iZXJfb2ZfdGFza3MSAhgBShsKFWNyZXdfbnVtYmVyX29m
|
||||
X2FnZW50cxICGAFKzgIKC2NyZXdfYWdlbnRzEr4CCrsCW3sia2V5IjogIjlkYzhjY2UwMzA0Njgx
|
||||
OTYwNDFiNGMzODBiNjE3Y2IwIiwgImlkIjogImI1ZGZkNmEyLTA1ZWYtNDIzNS1iZDVjLTI3ZTAy
|
||||
MGExYzk4ZiIsICJyb2xlIjogIkltYWdlIEFuYWx5c3QiLCAidmVyYm9zZT8iOiB0cnVlLCAibWF4
|
||||
X2l0ZXIiOiAyMCwgIm1heF9ycG0iOiBudWxsLCAiZnVuY3Rpb25fY2FsbGluZ19sbG0iOiAiIiwg
|
||||
ImxsbSI6ICJncHQtNG8iLCAiZGVsZWdhdGlvbl9lbmFibGVkPyI6IGZhbHNlLCAiYWxsb3dfY29k
|
||||
ZV9leGVjdXRpb24/IjogZmFsc2UsICJtYXhfcmV0cnlfbGltaXQiOiAyLCAidG9vbHNfbmFtZXMi
|
||||
OiBbXX1dSoICCgpjcmV3X3Rhc2tzEvMBCvABW3sia2V5IjogImE5YTc2Y2E2OTU3ZDBiZmZhNjll
|
||||
YWIyMGI2NjQ4MjJiIiwgImlkIjogIjJhMmQ4MDYzLTBkMmQtNDhmZi04NjJhLWNiOGM1NGEyMDYx
|
||||
NiIsICJhc3luY19leGVjdXRpb24/IjogZmFsc2UsICJodW1hbl9pbnB1dD8iOiBmYWxzZSwgImFn
|
||||
ZW50X3JvbGUiOiAiSW1hZ2UgQW5hbHlzdCIsICJhZ2VudF9rZXkiOiAiOWRjOGNjZTAzMDQ2ODE5
|
||||
NjA0MWI0YzM4MGI2MTdjYjAiLCAidG9vbHNfbmFtZXMiOiBbXX1degIYAYUBAAEAABKOAgoQj49w
|
||||
ugM/XFoNkMEnAmaPnRIIcFM/RoDbVhcqDFRhc2sgQ3JlYXRlZDABOViFR1nHoBoYQfgRSFnHoBoY
|
||||
Si4KCGNyZXdfa2V5EiIKIGUzOTU2N2I1MDUyOTA5Y2EzMzQwOTg0YjgzODk4MGVhSjEKB2NyZXdf
|
||||
aWQSJgokMDZlOWM3YWMtMTNkMi00ZTgxLWE3MjYtMGViMjJjN2U1ZDcwSi4KCHRhc2tfa2V5EiIK
|
||||
IGE5YTc2Y2E2OTU3ZDBiZmZhNjllYWIyMGI2NjQ4MjJiSjEKB3Rhc2tfaWQSJgokMmEyZDgwNjMt
|
||||
MGQyZC00OGZmLTg2MmEtY2I4YzU0YTIwNjE2egIYAYUBAAEAABKXAQoQQgYNvHzrhiz04CrSnkG0
|
||||
KBII9UsJM/96oEoqClRvb2wgVXNhZ2UwATkQPOFax6AaGEGAmupax6AaGEoaCg5jcmV3YWlfdmVy
|
||||
c2lvbhIICgYwLjk1LjBKIwoJdG9vbF9uYW1lEhYKFEFkZCBpbWFnZSB0byBjb250ZW50Sg4KCGF0
|
||||
dGVtcHRzEgIYAXoCGAGFAQABAAASpAcKEL8pSiN4H/umQhWexA4UYzoSCC+JqZKUlDffKgxDcmV3
|
||||
IENyZWF0ZWQwATnA9r9cx6AaGEGAJMhcx6AaGEoaCg5jcmV3YWlfdmVyc2lvbhIICgYwLjk1LjBK
|
||||
GgoOcHl0aG9uX3ZlcnNpb24SCAoGMy4xMi43Si4KCGNyZXdfa2V5EiIKIDAwYjk0NmJlNDQzNzE0
|
||||
YjNhNDdjMjAxMDFlYjAyZDY2SjEKB2NyZXdfaWQSJgokZDRhZDMyZTUtM2I1NS00OGQ0LTlmYjMt
|
||||
ZTVkOTY0ZGI5NzJhShwKDGNyZXdfcHJvY2VzcxIMCgpzZXF1ZW50aWFsShEKC2NyZXdfbWVtb3J5
|
||||
EgIQAEoaChRjcmV3X251bWJlcl9vZl90YXNrcxICGAFKGwoVY3Jld19udW1iZXJfb2ZfYWdlbnRz
|
||||
EgIYAUrPAgoLY3Jld19hZ2VudHMSvwIKvAJbeyJrZXkiOiAiNGI4YTdiODQwZjk0YmY3ODE4YjVk
|
||||
NTNmNjg5MjdmZDUiLCAiaWQiOiAiNjdlMDhiZDMtMzA5MS00ZTdhLWE4NjQtYTUyOGQ4ZDZlN2Y4
|
||||
IiwgInJvbGUiOiAiUmVwb3J0IFdyaXRlciIsICJ2ZXJib3NlPyI6IGZhbHNlLCAibWF4X2l0ZXIi
|
||||
OiAyMCwgIm1heF9ycG0iOiBudWxsLCAiZnVuY3Rpb25fY2FsbGluZ19sbG0iOiAiIiwgImxsbSI6
|
||||
ICJncHQtNG8iLCAiZGVsZWdhdGlvbl9lbmFibGVkPyI6IGZhbHNlLCAiYWxsb3dfY29kZV9leGVj
|
||||
dXRpb24/IjogZmFsc2UsICJtYXhfcmV0cnlfbGltaXQiOiAyLCAidG9vbHNfbmFtZXMiOiBbXX1d
|
||||
SoICCgpjcmV3X3Rhc2tzEvMBCvABW3sia2V5IjogImI3MTNjODJmZWI5MmM5ZjVjNThiNDBhOTc1
|
||||
NTZiN2FjIiwgImlkIjogIjUyZGMwN2ZjLWJjY2ItNDI4Mi1hZjllLWUyYTkxY2ViMzI0MCIsICJh
|
||||
c3luY19leGVjdXRpb24/IjogZmFsc2UsICJodW1hbl9pbnB1dD8iOiBmYWxzZSwgImFnZW50X3Jv
|
||||
bGUiOiAiUmVwb3J0IFdyaXRlciIsICJhZ2VudF9rZXkiOiAiNGI4YTdiODQwZjk0YmY3ODE4YjVk
|
||||
NTNmNjg5MjdmZDUiLCAidG9vbHNfbmFtZXMiOiBbXX1degIYAYUBAAEAABKOAgoQFiOJNSnPbaBo
|
||||
fje7Tx2DdBIIwjGhGgyR5BkqDFRhc2sgQ3JlYXRlZDABOaAq1FzHoBoYQah81FzHoBoYSi4KCGNy
|
||||
ZXdfa2V5EiIKIDAwYjk0NmJlNDQzNzE0YjNhNDdjMjAxMDFlYjAyZDY2SjEKB2NyZXdfaWQSJgok
|
||||
ZDRhZDMyZTUtM2I1NS00OGQ0LTlmYjMtZTVkOTY0ZGI5NzJhSi4KCHRhc2tfa2V5EiIKIGI3MTNj
|
||||
ODJmZWI5MmM5ZjVjNThiNDBhOTc1NTZiN2FjSjEKB3Rhc2tfaWQSJgokNTJkYzA3ZmMtYmNjYi00
|
||||
MjgyLWFmOWUtZTJhOTFjZWIzMjQwegIYAYUBAAEAABKOAgoQt0X92psFBaT0eyn1IxJl0RIIpDY4
|
||||
j2AlTioqDFRhc2sgQ3JlYXRlZDABOdgnPV/HoBoYQXi0PV/HoBoYSi4KCGNyZXdfa2V5EiIKIDAw
|
||||
Yjk0NmJlNDQzNzE0YjNhNDdjMjAxMDFlYjAyZDY2SjEKB2NyZXdfaWQSJgokZDRhZDMyZTUtM2I1
|
||||
NS00OGQ0LTlmYjMtZTVkOTY0ZGI5NzJhSi4KCHRhc2tfa2V5EiIKIGI3MTNjODJmZWI5MmM5ZjVj
|
||||
NThiNDBhOTc1NTZiN2FjSjEKB3Rhc2tfaWQSJgokNTJkYzA3ZmMtYmNjYi00MjgyLWFmOWUtZTJh
|
||||
OTFjZWIzMjQwegIYAYUBAAEAABKOAgoQZyIwBbsHH+6dumgTUJNVzxIIMAEwlT69bAwqDFRhc2sg
|
||||
Q3JlYXRlZDABOeh9u2HHoBoYQfghvGHHoBoYSi4KCGNyZXdfa2V5EiIKIDAwYjk0NmJlNDQzNzE0
|
||||
YjNhNDdjMjAxMDFlYjAyZDY2SjEKB2NyZXdfaWQSJgokZDRhZDMyZTUtM2I1NS00OGQ0LTlmYjMt
|
||||
ZTVkOTY0ZGI5NzJhSi4KCHRhc2tfa2V5EiIKIGI3MTNjODJmZWI5MmM5ZjVjNThiNDBhOTc1NTZi
|
||||
N2FjSjEKB3Rhc2tfaWQSJgokNTJkYzA3ZmMtYmNjYi00MjgyLWFmOWUtZTJhOTFjZWIzMjQwegIY
|
||||
AYUBAAEAABKOAgoQNmx90haqHtL8tj3Y948aIhIIaiFn4f7x7RAqDFRhc2sgQ3JlYXRlZDABOTgM
|
||||
nmTHoBoYQZCknmTHoBoYSi4KCGNyZXdfa2V5EiIKIDAwYjk0NmJlNDQzNzE0YjNhNDdjMjAxMDFl
|
||||
YjAyZDY2SjEKB2NyZXdfaWQSJgokZDRhZDMyZTUtM2I1NS00OGQ0LTlmYjMtZTVkOTY0ZGI5NzJh
|
||||
Si4KCHRhc2tfa2V5EiIKIGI3MTNjODJmZWI5MmM5ZjVjNThiNDBhOTc1NTZiN2FjSjEKB3Rhc2tf
|
||||
aWQSJgokNTJkYzA3ZmMtYmNjYi00MjgyLWFmOWUtZTJhOTFjZWIzMjQwegIYAYUBAAEAABKWBwoQ
|
||||
vt1TslFugf+idjOWhVfl9BIIGjt6tt0AKKkqDENyZXcgQ3JlYXRlZDABOWiz12fHoBoYQZj432fH
|
||||
oBoYShoKDmNyZXdhaV92ZXJzaW9uEggKBjAuOTUuMEoaCg5weXRob25fdmVyc2lvbhIICgYzLjEy
|
||||
LjdKLgoIY3Jld19rZXkSIgogZjVkZTY3ZTk5ODUwNTA3NmEyOTM3YjNmZGFhNzc1ZjFKMQoHY3Jl
|
||||
d19pZBImCiQ2MzJjYTc0MC1mNjg2LTRlNGQtOTBmYy00YjZkYmE5ZjViMGRKHAoMY3Jld19wcm9j
|
||||
ZXNzEgwKCnNlcXVlbnRpYWxKEQoLY3Jld19tZW1vcnkSAhAAShoKFGNyZXdfbnVtYmVyX29mX3Rh
|
||||
c2tzEgIYAUobChVjcmV3X251bWJlcl9vZl9hZ2VudHMSAhgBSsgCCgtjcmV3X2FnZW50cxK4Agq1
|
||||
Alt7ImtleSI6ICI2ZjYzZjNlMzU4M2E0NjJmZjNlNzY2MDcxYzgyMTJhZiIsICJpZCI6ICI1ZTZl
|
||||
NTMzNy1iZmMzLTRjZmYtODBlZi1hM2U5NDQ4YjBlYTMiLCAicm9sZSI6ICJXcml0ZXIiLCAidmVy
|
||||
Ym9zZT8iOiBmYWxzZSwgIm1heF9pdGVyIjogMjAsICJtYXhfcnBtIjogbnVsbCwgImZ1bmN0aW9u
|
||||
X2NhbGxpbmdfbGxtIjogIiIsICJsbG0iOiAiZ3B0LTRvIiwgImRlbGVnYXRpb25fZW5hYmxlZD8i
|
||||
OiBmYWxzZSwgImFsbG93X2NvZGVfZXhlY3V0aW9uPyI6IGZhbHNlLCAibWF4X3JldHJ5X2xpbWl0
|
||||
IjogMiwgInRvb2xzX25hbWVzIjogW119XUr7AQoKY3Jld190YXNrcxLsAQrpAVt7ImtleSI6ICIz
|
||||
ZjMyNzEyMDk2ZmFjYjliNGI2ZWE1NWI3OGViN2M4MCIsICJpZCI6ICI5NDRiZWRmNS0xZjZiLTQw
|
||||
OWEtOTE4Mi04YzMyZTM0MGZmMzQiLCAiYXN5bmNfZXhlY3V0aW9uPyI6IGZhbHNlLCAiaHVtYW5f
|
||||
aW5wdXQ/IjogZmFsc2UsICJhZ2VudF9yb2xlIjogIldyaXRlciIsICJhZ2VudF9rZXkiOiAiNmY2
|
||||
M2YzZTM1ODNhNDYyZmYzZTc2NjA3MWM4MjEyYWYiLCAidG9vbHNfbmFtZXMiOiBbXX1degIYAYUB
|
||||
AAEAABKOAgoQ4leDd4+yGvuAxat0Z7g/uhIInjgmW2jrDBIqDFRhc2sgQ3JlYXRlZDABOXCN62fH
|
||||
oBoYQXjf62fHoBoYSi4KCGNyZXdfa2V5EiIKIGY1ZGU2N2U5OTg1MDUwNzZhMjkzN2IzZmRhYTc3
|
||||
NWYxSjEKB2NyZXdfaWQSJgokNjMyY2E3NDAtZjY4Ni00ZTRkLTkwZmMtNGI2ZGJhOWY1YjBkSi4K
|
||||
CHRhc2tfa2V5EiIKIDNmMzI3MTIwOTZmYWNiOWI0YjZlYTU1Yjc4ZWI3YzgwSjEKB3Rhc2tfaWQS
|
||||
JgokOTQ0YmVkZjUtMWY2Yi00MDlhLTkxODItOGMzMmUzNDBmZjM0egIYAYUBAAEAABKOAgoQ/K3x
|
||||
az8rHR8RbOPAn3/V0xIIkOxMowIIFUoqDFRhc2sgQ3JlYXRlZDABOUCJ7WfHoBoYQcDH7WfHoBoY
|
||||
Si4KCGNyZXdfa2V5EiIKIGY1ZGU2N2U5OTg1MDUwNzZhMjkzN2IzZmRhYTc3NWYxSjEKB2NyZXdf
|
||||
aWQSJgokNjMyY2E3NDAtZjY4Ni00ZTRkLTkwZmMtNGI2ZGJhOWY1YjBkSi4KCHRhc2tfa2V5EiIK
|
||||
IDNmMzI3MTIwOTZmYWNiOWI0YjZlYTU1Yjc4ZWI3YzgwSjEKB3Rhc2tfaWQSJgokOTQ0YmVkZjUt
|
||||
MWY2Yi00MDlhLTkxODItOGMzMmUzNDBmZjM0egIYAYUBAAEAABKeBwoQ/q45KvZiCrfu5bu1k3u9
|
||||
PBII3yPQFsZi+ywqDENyZXcgQ3JlYXRlZDABObA3PWjHoBoYQUDYSGjHoBoYShoKDmNyZXdhaV92
|
||||
ZXJzaW9uEggKBjAuOTUuMEoaCg5weXRob25fdmVyc2lvbhIICgYzLjEyLjdKLgoIY3Jld19rZXkS
|
||||
IgogNzc2NTcyNTMwMGY2NjAwYjI5NjExYmI3ZTAyZDU2ZTZKMQoHY3Jld19pZBImCiQ3NDcwMDVh
|
||||
Yi1lODE0LTQ0YzItOWFlMy1lZTZkYWEzYmMxYjZKHAoMY3Jld19wcm9jZXNzEgwKCnNlcXVlbnRp
|
||||
YWxKEQoLY3Jld19tZW1vcnkSAhAAShoKFGNyZXdfbnVtYmVyX29mX3Rhc2tzEgIYAUobChVjcmV3
|
||||
X251bWJlcl9vZl9hZ2VudHMSAhgBSswCCgtjcmV3X2FnZW50cxK8Agq5Alt7ImtleSI6ICI3YjMz
|
||||
ZjY0ZGQwYjFiYTc4NWUwYmE4YmI1YjUyZjI0NiIsICJpZCI6ICI1ZTA0MzczNC02MGU1LTQwZWQt
|
||||
OGNlNS0wNjQ1MTNmMTkxMzciLCAicm9sZSI6ICJUZXN0IEFnZW50IiwgInZlcmJvc2U/IjogZmFs
|
||||
c2UsICJtYXhfaXRlciI6IDIwLCAibWF4X3JwbSI6IG51bGwsICJmdW5jdGlvbl9jYWxsaW5nX2xs
|
||||
bSI6ICIiLCAibGxtIjogImdwdC00byIsICJkZWxlZ2F0aW9uX2VuYWJsZWQ/IjogZmFsc2UsICJh
|
||||
bGxvd19jb2RlX2V4ZWN1dGlvbj8iOiBmYWxzZSwgIm1heF9yZXRyeV9saW1pdCI6IDIsICJ0b29s
|
||||
c19uYW1lcyI6IFtdfV1K/wEKCmNyZXdfdGFza3MS8AEK7QFbeyJrZXkiOiAiZDg3OTA0ZWU4MmNh
|
||||
NzVmZWQ1ODY4MTM3ZDRkYzEzNmYiLCAiaWQiOiAiNjdlZmEyZWEtZTQ0Ni00ZWI2LTg5YWMtMzA1
|
||||
ZDUwZjFkODMwIiwgImFzeW5jX2V4ZWN1dGlvbj8iOiBmYWxzZSwgImh1bWFuX2lucHV0PyI6IGZh
|
||||
bHNlLCAiYWdlbnRfcm9sZSI6ICJUZXN0IEFnZW50IiwgImFnZW50X2tleSI6ICI3YjMzZjY0ZGQw
|
||||
YjFiYTc4NWUwYmE4YmI1YjUyZjI0NiIsICJ0b29sc19uYW1lcyI6IFtdfV16AhgBhQEAAQAAEo4C
|
||||
ChAWSoeQUP+DNRqnwCDlpo82Egg4jJLBn5Yi2ioMVGFzayBDcmVhdGVkMAE5+I9WaMegGhhBAOJW
|
||||
aMegGhhKLgoIY3Jld19rZXkSIgogNzc2NTcyNTMwMGY2NjAwYjI5NjExYmI3ZTAyZDU2ZTZKMQoH
|
||||
Y3Jld19pZBImCiQ3NDcwMDVhYi1lODE0LTQ0YzItOWFlMy1lZTZkYWEzYmMxYjZKLgoIdGFza19r
|
||||
ZXkSIgogZDg3OTA0ZWU4MmNhNzVmZWQ1ODY4MTM3ZDRkYzEzNmZKMQoHdGFza19pZBImCiQ2N2Vm
|
||||
YTJlYS1lNDQ2LTRlYjYtODlhYy0zMDVkNTBmMWQ4MzB6AhgBhQEAAQAA
|
||||
body: '{"messages":[{"role":"system","content":"You are Test Agent. Test agent
|
||||
backstory\nYour personal goal is: Test agent goal"},{"role":"user","content":"\nCurrent
|
||||
Task: Test task description\n\nThis is the expected criteria for your final
|
||||
answer: Test expected output\nyou MUST return the actual complete content as
|
||||
the final answer, not a summary.\n\nProvide your complete response:"}],"model":"gpt-4.1-mini"}'
|
||||
headers:
|
||||
Accept:
|
||||
- '*/*'
|
||||
Accept-Encoding:
|
||||
- gzip, deflate
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Length:
|
||||
- '32247'
|
||||
Content-Type:
|
||||
- application/x-protobuf
|
||||
User-Agent:
|
||||
- OTel-OTLP-Exporter-Python/1.27.0
|
||||
method: POST
|
||||
uri: https://telemetry.crewai.com:4319/v1/traces
|
||||
response:
|
||||
body:
|
||||
string: "\n\0"
|
||||
headers:
|
||||
Content-Length:
|
||||
- '2'
|
||||
Content-Type:
|
||||
- application/x-protobuf
|
||||
Date:
|
||||
- Tue, 14 Jan 2025 17:56:25 GMT
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
- request:
|
||||
body: '{"messages": [{"role": "system", "content": "You are Test Agent. Test agent
|
||||
backstory\nYour personal goal is: Test agent goal\nTo give my best complete
|
||||
final answer to the task respond using the exact following format:\n\nThought:
|
||||
I now can give a great answer\nFinal Answer: Your final answer must be the great
|
||||
and the most complete as possible, it must be outcome described.\n\nI MUST use
|
||||
these formats, my job depends on it!"}, {"role": "user", "content": "\nCurrent
|
||||
Task: Test task description\n\nThis is the expect criteria for your final answer:
|
||||
Test expected output\nyou MUST return the actual complete content as the final
|
||||
answer, not a summary.\n\nBegin! This is VERY important to you, use the tools
|
||||
available and give your best Final Answer, your job depends on it!\n\nThought:"}],
|
||||
"model": "gpt-4o", "stop": ["\nObservation:"]}'
|
||||
headers:
|
||||
- X-USER-AGENT-XXX
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate
|
||||
- ACCEPT-ENCODING-XXX
|
||||
authorization:
|
||||
- AUTHORIZATION-XXX
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '838'
|
||||
- '407'
|
||||
content-type:
|
||||
- application/json
|
||||
cookie:
|
||||
- _cfuvid=SlnUP7AT9jJlQiN.Fm1c7MDyo78_hBRAz8PoabvHVSU-1736018539826-0.0.1.1-604800000
|
||||
host:
|
||||
- api.openai.com
|
||||
user-agent:
|
||||
- OpenAI/Python 1.59.6
|
||||
x-stainless-arch:
|
||||
- arm64
|
||||
- X-STAINLESS-ARCH-XXX
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- MacOS
|
||||
- X-STAINLESS-OS-XXX
|
||||
x-stainless-package-version:
|
||||
- 1.59.6
|
||||
x-stainless-raw-response:
|
||||
- 'true'
|
||||
- 1.83.0
|
||||
x-stainless-read-timeout:
|
||||
- X-STAINLESS-READ-TIMEOUT-XXX
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.12.7
|
||||
- 3.13.3
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: "{\n \"id\": \"chatcmpl-ApfRLkycSd0vwuTw50dfB5bgIoWiC\",\n \"object\"\
|
||||
: \"chat.completion\",\n \"created\": 1736877387,\n \"model\": \"gpt-4o-2024-08-06\"\
|
||||
,\n \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \
|
||||
\ \"role\": \"assistant\",\n \"content\": \"I now can give a great\
|
||||
\ answer \\nFinal Answer: The final answer must be the great and the most\
|
||||
\ complete as possible, it must be outcome described.\",\n \"refusal\"\
|
||||
: null\n },\n \"logprobs\": null,\n \"finish_reason\": \"stop\"\
|
||||
\n }\n ],\n \"usage\": {\n \"prompt_tokens\": 158,\n \"completion_tokens\"\
|
||||
: 31,\n \"total_tokens\": 189,\n \"prompt_tokens_details\": {\n \
|
||||
\ \"cached_tokens\": 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\"\
|
||||
: {\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"\
|
||||
accepted_prediction_tokens\": 0,\n \"rejected_prediction_tokens\": 0\n\
|
||||
\ }\n },\n \"service_tier\": \"default\",\n \"system_fingerprint\":\
|
||||
\ \"fp_50cad350e4\"\n}\n"
|
||||
string: "{\n \"id\": \"chatcmpl-DIjv3LqL0QS4iw3OM5b28B4VOMZPA\",\n \"object\":
|
||||
\"chat.completion\",\n \"created\": 1773358789,\n \"model\": \"gpt-4.1-mini-2025-04-14\",\n
|
||||
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||
\"assistant\",\n \"content\": \"Test expected output\",\n \"refusal\":
|
||||
null,\n \"annotations\": []\n },\n \"logprobs\": null,\n
|
||||
\ \"finish_reason\": \"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\":
|
||||
72,\n \"completion_tokens\": 3,\n \"total_tokens\": 75,\n \"prompt_tokens_details\":
|
||||
{\n \"cached_tokens\": 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\":
|
||||
{\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
||||
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\":
|
||||
\"default\",\n \"system_fingerprint\": \"fp_5e793402c9\"\n}\n"
|
||||
headers:
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-RAY:
|
||||
- 901f80a64cc6bd25-ATL
|
||||
CF-Ray:
|
||||
- 9db6a3f31e087b0e-EWR
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Tue, 14 Jan 2025 17:56:28 GMT
|
||||
- Thu, 12 Mar 2026 23:39:50 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- __cf_bm=A.PJUaUHPGyIr2pwNz44ei0seKXMH7czqXc5dA_MzD0-1736877388-1.0.1.1-jC2Lo7dl92z6qdY8mxRekSqg68TqMNsvyjPoNVXBfKNO6hHwL5BKWSBeA2i9hYWN2DBBLvHWeFXq1nXCKNcnlQ;
|
||||
path=/; expires=Tue, 14-Jan-25 18:26:28 GMT; domain=.api.openai.com; HttpOnly;
|
||||
Secure; SameSite=None
|
||||
- _cfuvid=kERLxnulwhkdPi_RxnQLZV8G2Zbub8n_KYkKSL6uke8-1736877388108-0.0.1.1-604800000;
|
||||
path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
|
||||
Strict-Transport-Security:
|
||||
- STS-XXX
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
- X-CONTENT-TYPE-XXX
|
||||
access-control-expose-headers:
|
||||
- X-Request-ID
|
||||
- ACCESS-CONTROL-XXX
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
openai-organization:
|
||||
- crewai-iuxna1
|
||||
- OPENAI-ORG-XXX
|
||||
openai-processing-ms:
|
||||
- '1020'
|
||||
- '360'
|
||||
openai-project:
|
||||
- OPENAI-PROJECT-XXX
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
strict-transport-security:
|
||||
- max-age=31536000; includeSubDomains; preload
|
||||
set-cookie:
|
||||
- SET-COOKIE-XXX
|
||||
x-openai-proxy-wasm:
|
||||
- v0.1
|
||||
x-ratelimit-limit-requests:
|
||||
- '10000'
|
||||
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||
x-ratelimit-limit-tokens:
|
||||
- '30000000'
|
||||
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||
x-ratelimit-remaining-requests:
|
||||
- '9999'
|
||||
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||
x-ratelimit-remaining-tokens:
|
||||
- '29999807'
|
||||
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||
x-ratelimit-reset-requests:
|
||||
- 6ms
|
||||
- X-RATELIMIT-RESET-REQUESTS-XXX
|
||||
x-ratelimit-reset-tokens:
|
||||
- 0s
|
||||
- X-RATELIMIT-RESET-TOKENS-XXX
|
||||
x-request-id:
|
||||
- req_4ceac9bc8ae57f631959b91d2ab63c4d
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
- request:
|
||||
body: '{"messages": [{"role": "system", "content": "You are Test Agent. Test agent
|
||||
backstory\nYour personal goal is: Test agent goal\nTo give my best complete
|
||||
final answer to the task respond using the exact following format:\n\nThought:
|
||||
I now can give a great answer\nFinal Answer: Your final answer must be the great
|
||||
and the most complete as possible, it must be outcome described.\n\nI MUST use
|
||||
these formats, my job depends on it!"}, {"role": "user", "content": "\nCurrent
|
||||
Task: Test task description\n\nThis is the expected criteria for your final
|
||||
answer: Test expected output\nyou MUST return the actual complete content as
|
||||
the final answer, not a summary.\n\nBegin! This is VERY important to you, use
|
||||
the tools available and give your best Final Answer, your job depends on it!\n\nThought:"}],
|
||||
"model": "gpt-4o", "stop": ["\nObservation:"]}'
|
||||
headers:
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '840'
|
||||
content-type:
|
||||
- application/json
|
||||
host:
|
||||
- api.openai.com
|
||||
user-agent:
|
||||
- OpenAI/Python 1.61.0
|
||||
x-stainless-arch:
|
||||
- x64
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- MacOS
|
||||
x-stainless-package-version:
|
||||
- 1.61.0
|
||||
x-stainless-raw-response:
|
||||
- 'true'
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.12.9
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: "{\n \"id\": \"chatcmpl-BExKOliqPgvHyozZaBu5oN50CHtsa\",\n \"object\"\
|
||||
: \"chat.completion\",\n \"created\": 1742904348,\n \"model\": \"gpt-4o-2024-08-06\"\
|
||||
,\n \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \
|
||||
\ \"role\": \"assistant\",\n \"content\": \"I now can give a great\
|
||||
\ answer \\nFinal Answer: Test expected output\",\n \"refusal\": null,\n\
|
||||
\ \"annotations\": []\n },\n \"logprobs\": null,\n \"\
|
||||
finish_reason\": \"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\"\
|
||||
: 158,\n \"completion_tokens\": 15,\n \"total_tokens\": 173,\n \"\
|
||||
prompt_tokens_details\": {\n \"cached_tokens\": 0,\n \"audio_tokens\"\
|
||||
: 0\n },\n \"completion_tokens_details\": {\n \"reasoning_tokens\"\
|
||||
: 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\": 0,\n\
|
||||
\ \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\"\
|
||||
: \"default\",\n \"system_fingerprint\": \"fp_90d33c15d4\"\n}\n"
|
||||
headers:
|
||||
CF-RAY:
|
||||
- 925e4749af02f227-GRU
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Tue, 25 Mar 2025 12:05:48 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Set-Cookie:
|
||||
- __cf_bm=VHa7Z7dJYptxXpaMxgldvK6HqIM.m74xpi.80N_EBDc-1742904348-1.0.1.1-VthD2riCSnAprFYhOZxfIrTjT33tybJHpHWB25Q_Hx4vuACCyF00tix6e6eorDReGcW3jb5cUzbGqYi47TrMsS4LYjxBv5eCo7cU9OuFajs;
|
||||
path=/; expires=Tue, 25-Mar-25 12:35:48 GMT; domain=.api.openai.com; HttpOnly;
|
||||
Secure; SameSite=None
|
||||
- _cfuvid=Is8fSaH3lU8yHyT3fI7cRZiDqIYSI6sPpzfzvEV8HMc-1742904348760-0.0.1.1-604800000;
|
||||
path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
access-control-expose-headers:
|
||||
- X-Request-ID
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
cf-cache-status:
|
||||
- DYNAMIC
|
||||
openai-organization:
|
||||
- crewai-iuxna1
|
||||
openai-processing-ms:
|
||||
- '377'
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
strict-transport-security:
|
||||
- max-age=31536000; includeSubDomains; preload
|
||||
x-ratelimit-limit-requests:
|
||||
- '50000'
|
||||
x-ratelimit-limit-tokens:
|
||||
- '150000000'
|
||||
x-ratelimit-remaining-requests:
|
||||
- '49999'
|
||||
x-ratelimit-remaining-tokens:
|
||||
- '149999822'
|
||||
x-ratelimit-reset-requests:
|
||||
- 1ms
|
||||
x-ratelimit-reset-tokens:
|
||||
- 0s
|
||||
x-request-id:
|
||||
- req_fd6b93e3b1a30868482c72306e7f63c2
|
||||
- X-REQUEST-ID-XXX
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
|
||||
@@ -45,78 +45,89 @@ interactions:
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.13.12
|
||||
- 3.13.3
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: "{\n \"id\": \"chatcmpl-DDFzCiMzYEJMnv9oV3KbMUwH6TGRO\",\n \"object\":
|
||||
\"chat.completion\",\n \"created\": 1772052086,\n \"model\": \"gpt-4.1-mini-2025-04-14\",\n
|
||||
string: "{\n \"id\": \"chatcmpl-DIjlpMNPWid0bFT3tJ0wlsOZelKz7\",\n \"object\":
|
||||
\"chat.completion\",\n \"created\": 1773358217,\n \"model\": \"gpt-4.1-mini-2025-04-14\",\n
|
||||
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||
\"assistant\",\n \"content\": \"- **The Rise of Autonomous AI Agents:
|
||||
Redefining Productivity and Creativity** \\n This article would dive into
|
||||
how autonomous AI agents\u2014intelligent software systems capable of independently
|
||||
performing complex tasks\u2014are transforming industries by augmenting human
|
||||
productivity and creativity. It would explore real-world use cases from automated
|
||||
content generation and customer support bots to AI-driven design and research
|
||||
assistants, illustrating how these agents reduce repetitive workload and open
|
||||
new avenues for innovation. The article could also analyze challenges such
|
||||
as ethical considerations, decision-making transparency, and integration with
|
||||
existing workflows, offering readers a comprehensive view of how autonomous
|
||||
AI agents are reshaping the future of work.\\n\\n- **Bridging Human-AI Collaboration:
|
||||
Designing AI Agents for Intuitive Interaction** \\n This piece would investigate
|
||||
the critical design principles behind successful human-AI collaboration, focusing
|
||||
on building AI agents that communicate and interact naturally with users.
|
||||
From natural language processing nuances to adaptive learning from user behavior,
|
||||
the article would examine how these technological advancements create seamless
|
||||
partnerships between humans and machines. Highlighting case studies in healthcare,
|
||||
finance, and creative industries, it would demonstrate the importance of trust,
|
||||
interpretability, and empathy in AI agent interfaces, emphasizing how better-designed
|
||||
interactions can dramatically improve adoption and effectiveness.\\n\\n- **The
|
||||
Ethical Frontier: Navigating Bias and Accountability in AI Agents** \\n Exploring
|
||||
the ethical implications of deploying AI agents at scale, this article would
|
||||
address pressing issues like algorithmic bias, privacy concerns, and accountability
|
||||
in autonomous decision-making. It would analyze how biases embedded in training
|
||||
data can propagate through AI agents, impacting critical outcomes in hiring,
|
||||
lending, and law enforcement. The article would also discuss emerging regulatory
|
||||
frameworks, best practices for auditing AI agents, and the role of interdisciplinary
|
||||
ethics teams in ensuring these technologies are fair, transparent, and responsible,
|
||||
helping readers grasp the societal responsibilities accompanying AI advancement.\\n\\n-
|
||||
**AI Agents in Startups: Driving Innovation and Competitive Advantage** \\n
|
||||
\ Focused on the startup ecosystem, this article would explore how emerging
|
||||
companies leverage AI agents to disrupt markets and scale rapidly with limited
|
||||
resources. It would profile startups using AI agents for customer acquisition,
|
||||
personalized marketing, operational automation, and product development, illustrating
|
||||
how these tools enable lean teams to achieve much more. The narrative would
|
||||
consider investment trends, challenges faced by startups incorporating AI
|
||||
agents, and strategies for balancing innovation with reliability, providing
|
||||
entrepreneurs and investors with valuable insights into harnessing AI agents
|
||||
for meaningful growth.\\n\\n- **From Data to Decision: How AI Agents Transform
|
||||
Business Intelligence** \\n This article would delve into the role of AI
|
||||
agents as intelligent intermediaries in business intelligence (BI) systems,
|
||||
automating data analysis and delivering actionable insights in real-time.
|
||||
It would explain how AI agents can parse vast datasets, identify trends, generate
|
||||
forecasts, and even suggest strategic decisions without constant human oversight.
|
||||
Highlighting innovations like conversational BI interfaces and predictive
|
||||
analytics agents, the article would underscore how businesses of all sizes
|
||||
can democratize data-driven decision-making, driving agility and competitive
|
||||
advantage in increasingly complex markets.\",\n \"refusal\": null,\n
|
||||
\ \"annotations\": []\n },\n \"logprobs\": null,\n \"finish_reason\":
|
||||
\"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\": 164,\n \"completion_tokens\":
|
||||
597,\n \"total_tokens\": 761,\n \"prompt_tokens_details\": {\n \"cached_tokens\":
|
||||
0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\":
|
||||
\"assistant\",\n \"content\": \"- **The Future of Autonomous AI Agents:
|
||||
From Task Automation to Decision Making** \\nExploring the evolving landscape
|
||||
of autonomous AI agents reveals a fascinating journey from simple task automation
|
||||
to complex decision-making systems capable of self-learning and adaptation.
|
||||
An article diving into this topic could unravel how cutting-edge advancements
|
||||
in reinforcement learning, natural language processing, and multi-agent systems
|
||||
are propelling AI agents beyond rigid scripts into dynamic collaborators and
|
||||
problem solvers. It would offer readers insights into real-world applications\u2014such
|
||||
as autonomous drones, financial trading bots, and personalized digital assistants\u2014and
|
||||
speculate on ethical considerations and regulatory frameworks shaping their
|
||||
future. This exploration emphasizes the transformative potential and the challenges
|
||||
that autonomous AI agents pose to industries and society at large.\\n\\n-
|
||||
**Bridging the Gap Between AI Agents and Human Collaboration** \\nAI agents
|
||||
are no longer isolated tools but increasingly integral collaborators in creative
|
||||
and professional workflows. An article tackling this theme could examine the
|
||||
latest progress in human-AI interaction models, including explainability,
|
||||
adaptability, and collaborative problem-solving. It would highlight how AI
|
||||
agents are augmenting human capabilities in fields like healthcare diagnostics,
|
||||
content creation, customer service, and software development. The narrative
|
||||
could also include case studies demonstrating successful AI-human partnerships
|
||||
along with the psychological and ergonomic aspects critical to designing AI
|
||||
agents that work harmoniously with humans. Such a piece would resonate deeply
|
||||
with readers interested in the symbiosis between artificial intelligence and
|
||||
human ingenuity.\\n\\n- **The Rise of AI Agents in Cybersecurity: Defense
|
||||
and Offense** \\nIn cybersecurity, AI agents are becoming indispensable,
|
||||
not only in defensive roles but also on the offensive front. An article focused
|
||||
on this area could deliver a comprehensive analysis of how AI agents detect
|
||||
and respond to threats in real time, employing techniques like anomaly detection,
|
||||
behavioral analysis, and automated incident response. Additionally, it would
|
||||
delve into the darker side: the use of AI agents by malicious actors for sophisticated
|
||||
cyber-attacks, including adaptive malware and social engineering bots. This
|
||||
dual perspective could provide a thrilling and nuanced investigation of the
|
||||
cybersecurity landscape dominated by AI, shedding light on strategic innovations,
|
||||
emerging threats, and the ongoing arms race between attackers and defenders.\\n\\n-
|
||||
**AI Agents in Startups: Revolutionizing Business Models and Customer Experience**
|
||||
\ \\nStartups are leveraging AI agents as a catalyst for innovation, scalability,
|
||||
and personalization, fundamentally transforming traditional business models.
|
||||
An article on this topic could survey real examples where AI agents streamline
|
||||
operations, enable hyper-personalized marketing, automate customer support,
|
||||
and generate actionable business insights. It would analyze how the integration
|
||||
of AI agents accelerates product-market fit through rapid iteration and data-driven
|
||||
decision-making. Moreover, the article could explore challenges unique to
|
||||
startup environments, such as resource constraints, technology adoption hurdles,
|
||||
and ethical considerations around AI deployment. This comprehensive view would
|
||||
inspire entrepreneurs and investors alike, spotlighting AI agents as game
|
||||
changers in the startup ecosystem.\\n\\n- **Ethical and Societal Implications
|
||||
of Delegating Decisions to AI Agents** \\nAs AI agents increasingly take
|
||||
on decision-making roles with significant real-world impact, ethical and societal
|
||||
questions come sharply into focus. An article on this theme could dissect
|
||||
challenges concerning accountability, transparency, bias, and human autonomy.
|
||||
It would provide an in-depth treatment of case studies where AI agents\u2019
|
||||
decisions led to unintended consequences or public backlash, and the regulatory
|
||||
and design frameworks proposed to mitigate these risks. Furthermore, the article
|
||||
could explore philosophical questions about trust, control, and the future
|
||||
relationship between humans and AI decision-makers. By unpacking the complex
|
||||
moral landscape surrounding AI agents, this piece would offer critical insight
|
||||
for policymakers, developers, and society at large.\",\n \"refusal\":
|
||||
null,\n \"annotations\": []\n },\n \"logprobs\": null,\n
|
||||
\ \"finish_reason\": \"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\":
|
||||
164,\n \"completion_tokens\": 720,\n \"total_tokens\": 884,\n \"prompt_tokens_details\":
|
||||
{\n \"cached_tokens\": 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\":
|
||||
{\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
||||
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\":
|
||||
\"default\",\n \"system_fingerprint\": \"fp_417e90869b\"\n}\n"
|
||||
\"default\",\n \"system_fingerprint\": \"fp_ae0f8c9a7b\"\n}\n"
|
||||
headers:
|
||||
CF-RAY:
|
||||
- CF-RAY-XXX
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-Ray:
|
||||
- 9db695fa4bf9b911-EWR
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 25 Feb 2026 20:41:39 GMT
|
||||
- Thu, 12 Mar 2026 23:30:27 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Strict-Transport-Security:
|
||||
@@ -129,12 +140,10 @@ interactions:
|
||||
- ACCESS-CONTROL-XXX
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
cf-cache-status:
|
||||
- DYNAMIC
|
||||
openai-organization:
|
||||
- OPENAI-ORG-XXX
|
||||
openai-processing-ms:
|
||||
- '13437'
|
||||
- '9053'
|
||||
openai-project:
|
||||
- OPENAI-PROJECT-XXX
|
||||
openai-version:
|
||||
@@ -176,68 +185,75 @@ interactions:
|
||||
list of ideas with their paragraph and your notes. task_expected_output: 5 bullet
|
||||
points with a paragraph for each idea. agent: Researcher agent_goal: Make the
|
||||
best research and analysis on content about AI and AI agents Task Output: -
|
||||
**The Rise of Autonomous AI Agents: Redefining Productivity and Creativity**
|
||||
\ \\n This article would dive into how autonomous AI agents\u2014intelligent
|
||||
software systems capable of independently performing complex tasks\u2014are
|
||||
transforming industries by augmenting human productivity and creativity. It
|
||||
would explore real-world use cases from automated content generation and customer
|
||||
support bots to AI-driven design and research assistants, illustrating how these
|
||||
agents reduce repetitive workload and open new avenues for innovation. The article
|
||||
could also analyze challenges such as ethical considerations, decision-making
|
||||
transparency, and integration with existing workflows, offering readers a comprehensive
|
||||
view of how autonomous AI agents are reshaping the future of work.\\n\\n- **Bridging
|
||||
Human-AI Collaboration: Designing AI Agents for Intuitive Interaction** \\n
|
||||
\ This piece would investigate the critical design principles behind successful
|
||||
human-AI collaboration, focusing on building AI agents that communicate and
|
||||
interact naturally with users. From natural language processing nuances to adaptive
|
||||
learning from user behavior, the article would examine how these technological
|
||||
advancements create seamless partnerships between humans and machines. Highlighting
|
||||
case studies in healthcare, finance, and creative industries, it would demonstrate
|
||||
the importance of trust, interpretability, and empathy in AI agent interfaces,
|
||||
emphasizing how better-designed interactions can dramatically improve adoption
|
||||
and effectiveness.\\n\\n- **The Ethical Frontier: Navigating Bias and Accountability
|
||||
in AI Agents** \\n Exploring the ethical implications of deploying AI agents
|
||||
at scale, this article would address pressing issues like algorithmic bias,
|
||||
privacy concerns, and accountability in autonomous decision-making. It would
|
||||
analyze how biases embedded in training data can propagate through AI agents,
|
||||
impacting critical outcomes in hiring, lending, and law enforcement. The article
|
||||
would also discuss emerging regulatory frameworks, best practices for auditing
|
||||
AI agents, and the role of interdisciplinary ethics teams in ensuring these
|
||||
technologies are fair, transparent, and responsible, helping readers grasp the
|
||||
societal responsibilities accompanying AI advancement.\\n\\n- **AI Agents in
|
||||
Startups: Driving Innovation and Competitive Advantage** \\n Focused on the
|
||||
startup ecosystem, this article would explore how emerging companies leverage
|
||||
AI agents to disrupt markets and scale rapidly with limited resources. It would
|
||||
profile startups using AI agents for customer acquisition, personalized marketing,
|
||||
operational automation, and product development, illustrating how these tools
|
||||
enable lean teams to achieve much more. The narrative would consider investment
|
||||
trends, challenges faced by startups incorporating AI agents, and strategies
|
||||
for balancing innovation with reliability, providing entrepreneurs and investors
|
||||
with valuable insights into harnessing AI agents for meaningful growth.\\n\\n-
|
||||
**From Data to Decision: How AI Agents Transform Business Intelligence** \\n
|
||||
\ This article would delve into the role of AI agents as intelligent intermediaries
|
||||
in business intelligence (BI) systems, automating data analysis and delivering
|
||||
actionable insights in real-time. It would explain how AI agents can parse vast
|
||||
datasets, identify trends, generate forecasts, and even suggest strategic decisions
|
||||
without constant human oversight. Highlighting innovations like conversational
|
||||
BI interfaces and predictive analytics agents, the article would underscore
|
||||
how businesses of all sizes can democratize data-driven decision-making, driving
|
||||
agility and competitive advantage in increasingly complex markets.\\n\\nThis
|
||||
is the expected criteria for your final answer: Evaluation Score from 1 to 10
|
||||
based on the performance of the agents on the tasks\\nyou MUST return the actual
|
||||
complete content as the final answer, not a summary.\\nFormat your final answer
|
||||
according to the following OpenAPI schema: {\\n \\\"properties\\\": {\\n \\\"quality\\\":
|
||||
{\\n \\\"description\\\": \\\"A score from 1 to 10 evaluating on completion,
|
||||
quality, and overall performance from the task_description and task_expected_output
|
||||
to the actual Task Output.\\\",\\n \\\"title\\\": \\\"Quality\\\",\\n \\\"type\\\":
|
||||
\\\"number\\\"\\n }\\n },\\n \\\"required\\\": [\\n \\\"quality\\\"\\n
|
||||
\ ],\\n \\\"title\\\": \\\"TaskEvaluationPydanticOutput\\\",\\n \\\"type\\\":
|
||||
\\\"object\\\",\\n \\\"additionalProperties\\\": false\\n}\\n\\nIMPORTANT:
|
||||
Preserve the original content exactly as-is. Do NOT rewrite, paraphrase, or
|
||||
modify the meaning of the content. Only structure it to match the schema format.\\n\\nDo
|
||||
not include the OpenAPI schema in the final output. Ensure the final output
|
||||
does not include any code block markers like ```json or ```python.\\n\\nProvide
|
||||
your complete response:\"}],\"model\":\"gpt-4o-mini\",\"response_format\":{\"type\":\"json_schema\",\"json_schema\":{\"schema\":{\"properties\":{\"quality\":{\"description\":\"A
|
||||
**The Future of Autonomous AI Agents: From Task Automation to Decision Making**
|
||||
\ \\nExploring the evolving landscape of autonomous AI agents reveals a fascinating
|
||||
journey from simple task automation to complex decision-making systems capable
|
||||
of self-learning and adaptation. An article diving into this topic could unravel
|
||||
how cutting-edge advancements in reinforcement learning, natural language processing,
|
||||
and multi-agent systems are propelling AI agents beyond rigid scripts into dynamic
|
||||
collaborators and problem solvers. It would offer readers insights into real-world
|
||||
applications\u2014such as autonomous drones, financial trading bots, and personalized
|
||||
digital assistants\u2014and speculate on ethical considerations and regulatory
|
||||
frameworks shaping their future. This exploration emphasizes the transformative
|
||||
potential and the challenges that autonomous AI agents pose to industries and
|
||||
society at large.\\n\\n- **Bridging the Gap Between AI Agents and Human Collaboration**
|
||||
\ \\nAI agents are no longer isolated tools but increasingly integral collaborators
|
||||
in creative and professional workflows. An article tackling this theme could
|
||||
examine the latest progress in human-AI interaction models, including explainability,
|
||||
adaptability, and collaborative problem-solving. It would highlight how AI agents
|
||||
are augmenting human capabilities in fields like healthcare diagnostics, content
|
||||
creation, customer service, and software development. The narrative could also
|
||||
include case studies demonstrating successful AI-human partnerships along with
|
||||
the psychological and ergonomic aspects critical to designing AI agents that
|
||||
work harmoniously with humans. Such a piece would resonate deeply with readers
|
||||
interested in the symbiosis between artificial intelligence and human ingenuity.\\n\\n-
|
||||
**The Rise of AI Agents in Cybersecurity: Defense and Offense** \\nIn cybersecurity,
|
||||
AI agents are becoming indispensable, not only in defensive roles but also on
|
||||
the offensive front. An article focused on this area could deliver a comprehensive
|
||||
analysis of how AI agents detect and respond to threats in real time, employing
|
||||
techniques like anomaly detection, behavioral analysis, and automated incident
|
||||
response. Additionally, it would delve into the darker side: the use of AI agents
|
||||
by malicious actors for sophisticated cyber-attacks, including adaptive malware
|
||||
and social engineering bots. This dual perspective could provide a thrilling
|
||||
and nuanced investigation of the cybersecurity landscape dominated by AI, shedding
|
||||
light on strategic innovations, emerging threats, and the ongoing arms race
|
||||
between attackers and defenders.\\n\\n- **AI Agents in Startups: Revolutionizing
|
||||
Business Models and Customer Experience** \\nStartups are leveraging AI agents
|
||||
as a catalyst for innovation, scalability, and personalization, fundamentally
|
||||
transforming traditional business models. An article on this topic could survey
|
||||
real examples where AI agents streamline operations, enable hyper-personalized
|
||||
marketing, automate customer support, and generate actionable business insights.
|
||||
It would analyze how the integration of AI agents accelerates product-market
|
||||
fit through rapid iteration and data-driven decision-making. Moreover, the article
|
||||
could explore challenges unique to startup environments, such as resource constraints,
|
||||
technology adoption hurdles, and ethical considerations around AI deployment.
|
||||
This comprehensive view would inspire entrepreneurs and investors alike, spotlighting
|
||||
AI agents as game changers in the startup ecosystem.\\n\\n- **Ethical and Societal
|
||||
Implications of Delegating Decisions to AI Agents** \\nAs AI agents increasingly
|
||||
take on decision-making roles with significant real-world impact, ethical and
|
||||
societal questions come sharply into focus. An article on this theme could dissect
|
||||
challenges concerning accountability, transparency, bias, and human autonomy.
|
||||
It would provide an in-depth treatment of case studies where AI agents\u2019
|
||||
decisions led to unintended consequences or public backlash, and the regulatory
|
||||
and design frameworks proposed to mitigate these risks. Furthermore, the article
|
||||
could explore philosophical questions about trust, control, and the future relationship
|
||||
between humans and AI decision-makers. By unpacking the complex moral landscape
|
||||
surrounding AI agents, this piece would offer critical insight for policymakers,
|
||||
developers, and society at large.\\n\\nThis is the expected criteria for your
|
||||
final answer: Evaluation Score from 1 to 10 based on the performance of the
|
||||
agents on the tasks\\nyou MUST return the actual complete content as the final
|
||||
answer, not a summary.\\nFormat your final answer according to the following
|
||||
OpenAPI schema: {\\n \\\"properties\\\": {\\n \\\"quality\\\": {\\n \\\"description\\\":
|
||||
\\\"A score from 1 to 10 evaluating on completion, quality, and overall performance
|
||||
from the task_description and task_expected_output to the actual Task Output.\\\",\\n
|
||||
\ \\\"title\\\": \\\"Quality\\\",\\n \\\"type\\\": \\\"number\\\"\\n
|
||||
\ }\\n },\\n \\\"required\\\": [\\n \\\"quality\\\"\\n ],\\n \\\"title\\\":
|
||||
\\\"TaskEvaluationPydanticOutput\\\",\\n \\\"type\\\": \\\"object\\\",\\n \\\"additionalProperties\\\":
|
||||
false\\n}\\n\\nIMPORTANT: Preserve the original content exactly as-is. Do NOT
|
||||
rewrite, paraphrase, or modify the meaning of the content. Only structure it
|
||||
to match the schema format.\\n\\nDo not include the OpenAPI schema in the final
|
||||
output. Ensure the final output does not include any code block markers like
|
||||
```json or ```python.\\n\\nProvide your complete response:\"}],\"model\":\"gpt-4o-mini\",\"response_format\":{\"type\":\"json_schema\",\"json_schema\":{\"schema\":{\"properties\":{\"quality\":{\"description\":\"A
|
||||
score from 1 to 10 evaluating on completion, quality, and overall performance
|
||||
from the task_description and task_expected_output to the actual Task Output.\",\"title\":\"Quality\",\"type\":\"number\"}},\"required\":[\"quality\"],\"title\":\"TaskEvaluationPydanticOutput\",\"type\":\"object\",\"additionalProperties\":false},\"name\":\"TaskEvaluationPydanticOutput\",\"strict\":true}},\"stream\":false}"
|
||||
headers:
|
||||
@@ -252,7 +268,7 @@ interactions:
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '6502'
|
||||
- '7184'
|
||||
content-type:
|
||||
- application/json
|
||||
host:
|
||||
@@ -276,31 +292,33 @@ interactions:
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.13.12
|
||||
- 3.13.3
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: "{\n \"id\": \"chatcmpl-DDFzQTBe214rOuf82URXmgkuNj5u4\",\n \"object\":
|
||||
\"chat.completion\",\n \"created\": 1772052100,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n
|
||||
string: "{\n \"id\": \"chatcmpl-DIjm0WxDVIL9NNzw98XHHh3cA4Yeh\",\n \"object\":
|
||||
\"chat.completion\",\n \"created\": 1773358228,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n
|
||||
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||
\"assistant\",\n \"content\": \"{\\\"quality\\\":9}\",\n \"refusal\":
|
||||
null,\n \"annotations\": []\n },\n \"logprobs\": null,\n
|
||||
\ \"finish_reason\": \"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\":
|
||||
1134,\n \"completion_tokens\": 5,\n \"total_tokens\": 1139,\n \"prompt_tokens_details\":
|
||||
1257,\n \"completion_tokens\": 5,\n \"total_tokens\": 1262,\n \"prompt_tokens_details\":
|
||||
{\n \"cached_tokens\": 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\":
|
||||
{\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
||||
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\":
|
||||
\"default\",\n \"system_fingerprint\": \"fp_bd4be55b21\"\n}\n"
|
||||
\"default\",\n \"system_fingerprint\": \"fp_e609550549\"\n}\n"
|
||||
headers:
|
||||
CF-RAY:
|
||||
- CF-RAY-XXX
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-Ray:
|
||||
- 9db696379bb48095-EWR
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 25 Feb 2026 20:41:40 GMT
|
||||
- Thu, 12 Mar 2026 23:30:28 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Strict-Transport-Security:
|
||||
@@ -313,12 +331,10 @@ interactions:
|
||||
- ACCESS-CONTROL-XXX
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
cf-cache-status:
|
||||
- DYNAMIC
|
||||
openai-organization:
|
||||
- OPENAI-ORG-XXX
|
||||
openai-processing-ms:
|
||||
- '241'
|
||||
- '380'
|
||||
openai-project:
|
||||
- OPENAI-PROJECT-XXX
|
||||
openai-version:
|
||||
@@ -356,54 +372,62 @@ interactions:
|
||||
paragraph and your notes.\\n\\nThis is the expected criteria for your final
|
||||
answer: 5 bullet points with a paragraph for each idea.\\nyou MUST return the
|
||||
actual complete content as the final answer, not a summary.\\n\\nProvide your
|
||||
complete response:\"},{\"role\":\"assistant\",\"content\":\"- **The Rise of
|
||||
Autonomous AI Agents: Redefining Productivity and Creativity** \\n This article
|
||||
would dive into how autonomous AI agents\u2014intelligent software systems capable
|
||||
of independently performing complex tasks\u2014are transforming industries by
|
||||
augmenting human productivity and creativity. It would explore real-world use
|
||||
cases from automated content generation and customer support bots to AI-driven
|
||||
design and research assistants, illustrating how these agents reduce repetitive
|
||||
workload and open new avenues for innovation. The article could also analyze
|
||||
challenges such as ethical considerations, decision-making transparency, and
|
||||
integration with existing workflows, offering readers a comprehensive view of
|
||||
how autonomous AI agents are reshaping the future of work.\\n\\n- **Bridging
|
||||
Human-AI Collaboration: Designing AI Agents for Intuitive Interaction** \\n
|
||||
\ This piece would investigate the critical design principles behind successful
|
||||
human-AI collaboration, focusing on building AI agents that communicate and
|
||||
interact naturally with users. From natural language processing nuances to adaptive
|
||||
learning from user behavior, the article would examine how these technological
|
||||
advancements create seamless partnerships between humans and machines. Highlighting
|
||||
case studies in healthcare, finance, and creative industries, it would demonstrate
|
||||
the importance of trust, interpretability, and empathy in AI agent interfaces,
|
||||
emphasizing how better-designed interactions can dramatically improve adoption
|
||||
and effectiveness.\\n\\n- **The Ethical Frontier: Navigating Bias and Accountability
|
||||
in AI Agents** \\n Exploring the ethical implications of deploying AI agents
|
||||
at scale, this article would address pressing issues like algorithmic bias,
|
||||
privacy concerns, and accountability in autonomous decision-making. It would
|
||||
analyze how biases embedded in training data can propagate through AI agents,
|
||||
impacting critical outcomes in hiring, lending, and law enforcement. The article
|
||||
would also discuss emerging regulatory frameworks, best practices for auditing
|
||||
AI agents, and the role of interdisciplinary ethics teams in ensuring these
|
||||
technologies are fair, transparent, and responsible, helping readers grasp the
|
||||
societal responsibilities accompanying AI advancement.\\n\\n- **AI Agents in
|
||||
Startups: Driving Innovation and Competitive Advantage** \\n Focused on the
|
||||
startup ecosystem, this article would explore how emerging companies leverage
|
||||
AI agents to disrupt markets and scale rapidly with limited resources. It would
|
||||
profile startups using AI agents for customer acquisition, personalized marketing,
|
||||
operational automation, and product development, illustrating how these tools
|
||||
enable lean teams to achieve much more. The narrative would consider investment
|
||||
trends, challenges faced by startups incorporating AI agents, and strategies
|
||||
for balancing innovation with reliability, providing entrepreneurs and investors
|
||||
with valuable insights into harnessing AI agents for meaningful growth.\\n\\n-
|
||||
**From Data to Decision: How AI Agents Transform Business Intelligence** \\n
|
||||
\ This article would delve into the role of AI agents as intelligent intermediaries
|
||||
in business intelligence (BI) systems, automating data analysis and delivering
|
||||
actionable insights in real-time. It would explain how AI agents can parse vast
|
||||
datasets, identify trends, generate forecasts, and even suggest strategic decisions
|
||||
without constant human oversight. Highlighting innovations like conversational
|
||||
BI interfaces and predictive analytics agents, the article would underscore
|
||||
how businesses of all sizes can democratize data-driven decision-making, driving
|
||||
agility and competitive advantage in increasingly complex markets.\"},{\"role\":\"system\",\"content\":\"You
|
||||
complete response:\"},{\"role\":\"assistant\",\"content\":\"- **The Future of
|
||||
Autonomous AI Agents: From Task Automation to Decision Making** \\nExploring
|
||||
the evolving landscape of autonomous AI agents reveals a fascinating journey
|
||||
from simple task automation to complex decision-making systems capable of self-learning
|
||||
and adaptation. An article diving into this topic could unravel how cutting-edge
|
||||
advancements in reinforcement learning, natural language processing, and multi-agent
|
||||
systems are propelling AI agents beyond rigid scripts into dynamic collaborators
|
||||
and problem solvers. It would offer readers insights into real-world applications\u2014such
|
||||
as autonomous drones, financial trading bots, and personalized digital assistants\u2014and
|
||||
speculate on ethical considerations and regulatory frameworks shaping their
|
||||
future. This exploration emphasizes the transformative potential and the challenges
|
||||
that autonomous AI agents pose to industries and society at large.\\n\\n- **Bridging
|
||||
the Gap Between AI Agents and Human Collaboration** \\nAI agents are no longer
|
||||
isolated tools but increasingly integral collaborators in creative and professional
|
||||
workflows. An article tackling this theme could examine the latest progress
|
||||
in human-AI interaction models, including explainability, adaptability, and
|
||||
collaborative problem-solving. It would highlight how AI agents are augmenting
|
||||
human capabilities in fields like healthcare diagnostics, content creation,
|
||||
customer service, and software development. The narrative could also include
|
||||
case studies demonstrating successful AI-human partnerships along with the psychological
|
||||
and ergonomic aspects critical to designing AI agents that work harmoniously
|
||||
with humans. Such a piece would resonate deeply with readers interested in the
|
||||
symbiosis between artificial intelligence and human ingenuity.\\n\\n- **The
|
||||
Rise of AI Agents in Cybersecurity: Defense and Offense** \\nIn cybersecurity,
|
||||
AI agents are becoming indispensable, not only in defensive roles but also on
|
||||
the offensive front. An article focused on this area could deliver a comprehensive
|
||||
analysis of how AI agents detect and respond to threats in real time, employing
|
||||
techniques like anomaly detection, behavioral analysis, and automated incident
|
||||
response. Additionally, it would delve into the darker side: the use of AI agents
|
||||
by malicious actors for sophisticated cyber-attacks, including adaptive malware
|
||||
and social engineering bots. This dual perspective could provide a thrilling
|
||||
and nuanced investigation of the cybersecurity landscape dominated by AI, shedding
|
||||
light on strategic innovations, emerging threats, and the ongoing arms race
|
||||
between attackers and defenders.\\n\\n- **AI Agents in Startups: Revolutionizing
|
||||
Business Models and Customer Experience** \\nStartups are leveraging AI agents
|
||||
as a catalyst for innovation, scalability, and personalization, fundamentally
|
||||
transforming traditional business models. An article on this topic could survey
|
||||
real examples where AI agents streamline operations, enable hyper-personalized
|
||||
marketing, automate customer support, and generate actionable business insights.
|
||||
It would analyze how the integration of AI agents accelerates product-market
|
||||
fit through rapid iteration and data-driven decision-making. Moreover, the article
|
||||
could explore challenges unique to startup environments, such as resource constraints,
|
||||
technology adoption hurdles, and ethical considerations around AI deployment.
|
||||
This comprehensive view would inspire entrepreneurs and investors alike, spotlighting
|
||||
AI agents as game changers in the startup ecosystem.\\n\\n- **Ethical and Societal
|
||||
Implications of Delegating Decisions to AI Agents** \\nAs AI agents increasingly
|
||||
take on decision-making roles with significant real-world impact, ethical and
|
||||
societal questions come sharply into focus. An article on this theme could dissect
|
||||
challenges concerning accountability, transparency, bias, and human autonomy.
|
||||
It would provide an in-depth treatment of case studies where AI agents\u2019
|
||||
decisions led to unintended consequences or public backlash, and the regulatory
|
||||
and design frameworks proposed to mitigate these risks. Furthermore, the article
|
||||
could explore philosophical questions about trust, control, and the future relationship
|
||||
between humans and AI decision-makers. By unpacking the complex moral landscape
|
||||
surrounding AI agents, this piece would offer critical insight for policymakers,
|
||||
developers, and society at large.\"},{\"role\":\"system\",\"content\":\"You
|
||||
are Researcher. You're an expert researcher, specialized in technology, software
|
||||
engineering, AI and startups. You work as a freelancer and is now working on
|
||||
doing research and analysis for a new customer.\\nYour personal goal is: Make
|
||||
@@ -427,7 +451,7 @@ interactions:
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '5482'
|
||||
- '6164'
|
||||
content-type:
|
||||
- application/json
|
||||
cookie:
|
||||
@@ -451,88 +475,88 @@ interactions:
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.13.12
|
||||
- 3.13.3
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: "{\n \"id\": \"chatcmpl-DDFzR5eiizUskHHVDIqVHcFSGrZxy\",\n \"object\":
|
||||
\"chat.completion\",\n \"created\": 1772052101,\n \"model\": \"gpt-4.1-mini-2025-04-14\",\n
|
||||
string: "{\n \"id\": \"chatcmpl-DIjm02rlgGyxm8CuBCxW7VcLsZ2UH\",\n \"object\":
|
||||
\"chat.completion\",\n \"created\": 1773358228,\n \"model\": \"gpt-4.1-mini-2025-04-14\",\n
|
||||
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||
\"assistant\",\n \"content\": \"- **The Evolution of AI Agents: From
|
||||
Simple Automation to Context-Aware Decision Makers** \\n This article would
|
||||
chart the remarkable progression of AI agents, tracing their journey from
|
||||
rule-based automation systems to sophisticated, context-aware entities capable
|
||||
of reasoning and adapting within complex environments. By exploring advances
|
||||
in reinforcement learning, natural language understanding, and multi-agent
|
||||
collaboration, readers would gain insight into how modern AI agents operate
|
||||
with increasing autonomy. The piece could include cutting-edge research examples
|
||||
and practical applications, illustrating how this evolution unlocks new possibilities
|
||||
across industries such as healthcare, logistics, and customer service, truly
|
||||
showcasing the expanding potential and impact of AI agents in everyday life.\\n\\n-
|
||||
**Designing Trustworthy AI Agents: Balancing Transparency and Performance**
|
||||
\ \\n Focusing on the critical issue of trust, this article would explore
|
||||
the tension between creating AI agents that offer high performance and those
|
||||
designed to be transparent and explainable to users. It would delve into techniques
|
||||
like explainable AI (XAI), confidence scoring, and user-centric design principles
|
||||
that foster trust and accountability. With a mix of theoretical insights and
|
||||
real-world implementations, the article would highlight how companies tackle
|
||||
challenges in deploying AI agents responsibly\u2014especially in sensitive
|
||||
domains like finance, law enforcement, and healthcare\u2014demonstrating how
|
||||
trustworthiness can become a competitive advantage in AI-driven services.\\n\\n-
|
||||
**AI Agents as Personal Productivity Assistants: Beyond Scheduling and Reminders**
|
||||
\ \\n This topic examines how AI agents are evolving from basic virtual assistants
|
||||
to powerful personal productivity coaches that understand context, anticipate
|
||||
needs, and proactively manage tasks. The article would investigate advances
|
||||
in multi-modal understanding, emotional intelligence, and continuous learning
|
||||
that enable AI agents to provide nuanced support in time management, email
|
||||
triage, project coordination, and even creative brainstorming. Case studies
|
||||
from popular platforms and startups would showcase how this new generation
|
||||
of AI agents is revolutionizing daily workflows for professionals across sectors,
|
||||
offering readers a forward-looking perspective on the future of personal digital
|
||||
assistance.\\n\\n- **Collaborative AI Agents in Multi-Agent Systems: Driving
|
||||
Complex Problem Solving** \\n This article would focus on the growing field
|
||||
of multi-agent AI systems, where multiple AI agents communicate, negotiate,
|
||||
and collaborate to solve problems that are too complex for a single agent.
|
||||
It would highlight research advances in swarm intelligence, decentralized
|
||||
decision-making, and cooperative game theory, and demonstrate practical applications
|
||||
ranging from autonomous vehicle fleets to smart grid management and disaster
|
||||
response coordination. By unpacking these complex interactions, the article
|
||||
would engage readers with the fascinating dynamics of AI ecosystems and the
|
||||
promise of collaborative agents to address society\u2019s grand challenges.\\n\\n-
|
||||
**Startups Building Next-Gen AI Agents: Innovating at the Intersection of
|
||||
AI and User Experience** \\n Highlighting startups at the forefront of AI
|
||||
agent technology, this article would provide an in-depth look at how these
|
||||
ventures blend cutting-edge artificial intelligence with seamless user experiences
|
||||
to disrupt traditional markets. It would examine how startups harness advances
|
||||
in natural language processing, reinforcement learning, and personalized modeling
|
||||
to create AI agents that feel intuitive and human-like, powering applications
|
||||
in healthcare, education, finance, and customer engagement. The article would
|
||||
also discuss funding trends, go-to-market strategies, and technological challenges,
|
||||
offering entrepreneurs, investors, and technologists valuable insights into
|
||||
what it takes to succeed in the burgeoning AI agent landscape.\\n\\n**Notes:**
|
||||
\ \\nThese ideas are crafted to cover a broad spectrum of AI agent-related
|
||||
topics, combining technical depth with real-world relevance. Each paragraph
|
||||
aims to showcase the potential richness, relevance, and appeal of a full article,
|
||||
ensuring the content would engage a diverse readership, from AI researchers
|
||||
and software engineers to startup founders and business leaders interested
|
||||
in AI innovation.\",\n \"refusal\": null,\n \"annotations\":
|
||||
\"assistant\",\n \"content\": \"- **How AI Agents are Shaping the Future
|
||||
of Personalized Learning** \\nAn article exploring how AI agents are revolutionizing
|
||||
personalized learning could captivate readers by detailing how these intelligent
|
||||
systems adapt educational content in real time to meet individual learner
|
||||
needs. By combining adaptive learning algorithms, natural language understanding,
|
||||
and behavioral analytics, AI agents are transforming classrooms and online
|
||||
platforms alike, providing personalized feedback, pacing, and even motivation
|
||||
strategies. This piece would dive into current implementations, such as AI
|
||||
tutors and automated grading, and forecast the potential for lifelong learning
|
||||
companions that evolve alongside the learner\u2019s growth. Readers would
|
||||
gain a comprehensive view of how AI agents can democratize access to education
|
||||
while enhancing efficacy across diverse learning environments.\\n\\n- **The
|
||||
Role of AI Agents in Accelerating Drug Discovery and Healthcare Innovation**
|
||||
\ \\nThis article would provide a compelling exploration of how AI agents
|
||||
are accelerating drug discovery by automating complex data analysis, predicting
|
||||
molecular interactions, and optimizing clinical trial design. It would highlight
|
||||
cutting-edge examples where AI agents collaborate with humans to speed up
|
||||
identifying promising compounds, reducing costs, and shortening development
|
||||
cycles. Beyond pharmaceuticals, the article could explore AI agents\u2019
|
||||
expanding roles in personalized medicine, patient monitoring, and diagnostic
|
||||
support. With healthcare challenges mounting globally, this topic offers high
|
||||
relevance and excitement by showcasing how AI agents act as catalysts for
|
||||
breakthroughs that can save lives and transform medical care.\\n\\n- **Collaborative
|
||||
AI Agents: The Next Frontier in Software Development and DevOps** \\nAn in-depth
|
||||
article on collaborative AI agents in software engineering would showcase
|
||||
how these tools enhance productivity and code quality by automating routine
|
||||
tasks, catching bugs, and assisting in code reviews. It could examine emerging
|
||||
AI agents designed for pair programming, continuous integration, deployment
|
||||
automation, and intelligent testing. By integrating seamlessly into DevOps
|
||||
pipelines, these AI collaborators reduce human error, speed up delivery cycles,
|
||||
and enable developers to focus on innovation. The piece would also discuss
|
||||
challenges like trust, explainability, and maintaining human oversight, appealing
|
||||
to software engineers and technology leaders eager to understand the practical
|
||||
implications of AI agents in development workflows.\\n\\n- **AI Agents and
|
||||
the Evolution of Customer Experience in Digital Businesses** \\nThis article
|
||||
would explore the transformative role AI agents play in reshaping customer
|
||||
experience for digital-first businesses. It could cover AI-powered chatbots,
|
||||
recommendation engines, sentiment analysis tools, and personalized marketing
|
||||
agents, illustrating how these intelligent systems enhance engagement and
|
||||
satisfaction while reducing operational costs. By weaving in real-world case
|
||||
studies and data, the article would demonstrate how AI agents help companies
|
||||
anticipate customer needs, resolve issues proactively, and create seamless
|
||||
omnichannel interactions. The narrative could also touch on the balance between
|
||||
automation and human touch, offering strategic insights for businesses aiming
|
||||
to harness AI agents without compromising brand loyalty and trust.\\n\\n-
|
||||
**Ethical Frameworks for the Deployment of Autonomous AI Agents in Society**
|
||||
\ \\nThis article would address the critical and timely topic of ethical considerations
|
||||
surrounding the deployment of autonomous AI agents in public and private spheres.
|
||||
It would systematically analyze issues such as accountability, transparency,
|
||||
fairness, privacy, and unintended consequences when AI agents make decisions
|
||||
or act autonomously. The article could feature interviews with ethicists,
|
||||
policymakers, and researchers, and review current regulatory efforts and standards
|
||||
shaping AI governance. By unpacking this complex terrain, the article would
|
||||
provide a thoughtful, multidisciplinary perspective crucial for stakeholders
|
||||
aiming to responsibly develop and deploy AI agents in ways that align with
|
||||
societal values and legal frameworks.\",\n \"refusal\": null,\n \"annotations\":
|
||||
[]\n },\n \"logprobs\": null,\n \"finish_reason\": \"stop\"\n
|
||||
\ }\n ],\n \"usage\": {\n \"prompt_tokens\": 926,\n \"completion_tokens\":
|
||||
727,\n \"total_tokens\": 1653,\n \"prompt_tokens_details\": {\n \"cached_tokens\":
|
||||
\ }\n ],\n \"usage\": {\n \"prompt_tokens\": 1049,\n \"completion_tokens\":
|
||||
677,\n \"total_tokens\": 1726,\n \"prompt_tokens_details\": {\n \"cached_tokens\":
|
||||
0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\":
|
||||
{\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
||||
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\":
|
||||
\"default\",\n \"system_fingerprint\": \"fp_417e90869b\"\n}\n"
|
||||
\"default\",\n \"system_fingerprint\": \"fp_ae0f8c9a7b\"\n}\n"
|
||||
headers:
|
||||
CF-RAY:
|
||||
- CF-RAY-XXX
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-Ray:
|
||||
- 9db696410a2db911-EWR
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 25 Feb 2026 20:41:50 GMT
|
||||
- Thu, 12 Mar 2026 23:30:38 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Strict-Transport-Security:
|
||||
@@ -545,12 +569,10 @@ interactions:
|
||||
- ACCESS-CONTROL-XXX
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
cf-cache-status:
|
||||
- DYNAMIC
|
||||
openai-organization:
|
||||
- OPENAI-ORG-XXX
|
||||
openai-processing-ms:
|
||||
- '9082'
|
||||
- '9221'
|
||||
openai-project:
|
||||
- OPENAI-PROJECT-XXX
|
||||
openai-version:
|
||||
@@ -590,76 +612,74 @@ interactions:
|
||||
list of ideas with their paragraph and your notes. task_expected_output: 5 bullet
|
||||
points with a paragraph for each idea. agent: Researcher agent_goal: Make the
|
||||
best research and analysis on content about AI and AI agents Task Output: -
|
||||
**The Evolution of AI Agents: From Simple Automation to Context-Aware Decision
|
||||
Makers** \\n This article would chart the remarkable progression of AI agents,
|
||||
tracing their journey from rule-based automation systems to sophisticated, context-aware
|
||||
entities capable of reasoning and adapting within complex environments. By exploring
|
||||
advances in reinforcement learning, natural language understanding, and multi-agent
|
||||
collaboration, readers would gain insight into how modern AI agents operate
|
||||
with increasing autonomy. The piece could include cutting-edge research examples
|
||||
and practical applications, illustrating how this evolution unlocks new possibilities
|
||||
across industries such as healthcare, logistics, and customer service, truly
|
||||
showcasing the expanding potential and impact of AI agents in everyday life.\\n\\n-
|
||||
**Designing Trustworthy AI Agents: Balancing Transparency and Performance**
|
||||
\ \\n Focusing on the critical issue of trust, this article would explore the
|
||||
tension between creating AI agents that offer high performance and those designed
|
||||
to be transparent and explainable to users. It would delve into techniques like
|
||||
explainable AI (XAI), confidence scoring, and user-centric design principles
|
||||
that foster trust and accountability. With a mix of theoretical insights and
|
||||
real-world implementations, the article would highlight how companies tackle
|
||||
challenges in deploying AI agents responsibly\u2014especially in sensitive domains
|
||||
like finance, law enforcement, and healthcare\u2014demonstrating how trustworthiness
|
||||
can become a competitive advantage in AI-driven services.\\n\\n- **AI Agents
|
||||
as Personal Productivity Assistants: Beyond Scheduling and Reminders** \\n
|
||||
\ This topic examines how AI agents are evolving from basic virtual assistants
|
||||
to powerful personal productivity coaches that understand context, anticipate
|
||||
needs, and proactively manage tasks. The article would investigate advances
|
||||
in multi-modal understanding, emotional intelligence, and continuous learning
|
||||
that enable AI agents to provide nuanced support in time management, email triage,
|
||||
project coordination, and even creative brainstorming. Case studies from popular
|
||||
platforms and startups would showcase how this new generation of AI agents is
|
||||
revolutionizing daily workflows for professionals across sectors, offering readers
|
||||
a forward-looking perspective on the future of personal digital assistance.\\n\\n-
|
||||
**Collaborative AI Agents in Multi-Agent Systems: Driving Complex Problem Solving**
|
||||
\ \\n This article would focus on the growing field of multi-agent AI systems,
|
||||
where multiple AI agents communicate, negotiate, and collaborate to solve problems
|
||||
that are too complex for a single agent. It would highlight research advances
|
||||
in swarm intelligence, decentralized decision-making, and cooperative game theory,
|
||||
and demonstrate practical applications ranging from autonomous vehicle fleets
|
||||
to smart grid management and disaster response coordination. By unpacking these
|
||||
complex interactions, the article would engage readers with the fascinating
|
||||
dynamics of AI ecosystems and the promise of collaborative agents to address
|
||||
society\u2019s grand challenges.\\n\\n- **Startups Building Next-Gen AI Agents:
|
||||
Innovating at the Intersection of AI and User Experience** \\n Highlighting
|
||||
startups at the forefront of AI agent technology, this article would provide
|
||||
an in-depth look at how these ventures blend cutting-edge artificial intelligence
|
||||
with seamless user experiences to disrupt traditional markets. It would examine
|
||||
how startups harness advances in natural language processing, reinforcement
|
||||
learning, and personalized modeling to create AI agents that feel intuitive
|
||||
and human-like, powering applications in healthcare, education, finance, and
|
||||
customer engagement. The article would also discuss funding trends, go-to-market
|
||||
strategies, and technological challenges, offering entrepreneurs, investors,
|
||||
and technologists valuable insights into what it takes to succeed in the burgeoning
|
||||
AI agent landscape.\\n\\n**Notes:** \\nThese ideas are crafted to cover a broad
|
||||
spectrum of AI agent-related topics, combining technical depth with real-world
|
||||
relevance. Each paragraph aims to showcase the potential richness, relevance,
|
||||
and appeal of a full article, ensuring the content would engage a diverse readership,
|
||||
from AI researchers and software engineers to startup founders and business
|
||||
leaders interested in AI innovation.\\n\\nThis is the expected criteria for
|
||||
your final answer: Evaluation Score from 1 to 10 based on the performance of
|
||||
the agents on the tasks\\nyou MUST return the actual complete content as the
|
||||
final answer, not a summary.\\nFormat your final answer according to the following
|
||||
OpenAPI schema: {\\n \\\"properties\\\": {\\n \\\"quality\\\": {\\n \\\"description\\\":
|
||||
\\\"A score from 1 to 10 evaluating on completion, quality, and overall performance
|
||||
from the task_description and task_expected_output to the actual Task Output.\\\",\\n
|
||||
\ \\\"title\\\": \\\"Quality\\\",\\n \\\"type\\\": \\\"number\\\"\\n
|
||||
\ }\\n },\\n \\\"required\\\": [\\n \\\"quality\\\"\\n ],\\n \\\"title\\\":
|
||||
\\\"TaskEvaluationPydanticOutput\\\",\\n \\\"type\\\": \\\"object\\\",\\n \\\"additionalProperties\\\":
|
||||
false\\n}\\n\\nIMPORTANT: Preserve the original content exactly as-is. Do NOT
|
||||
rewrite, paraphrase, or modify the meaning of the content. Only structure it
|
||||
to match the schema format.\\n\\nDo not include the OpenAPI schema in the final
|
||||
output. Ensure the final output does not include any code block markers like
|
||||
```json or ```python.\\n\\nProvide your complete response:\"}],\"model\":\"gpt-4o-mini\",\"response_format\":{\"type\":\"json_schema\",\"json_schema\":{\"schema\":{\"properties\":{\"quality\":{\"description\":\"A
|
||||
**How AI Agents are Shaping the Future of Personalized Learning** \\nAn article
|
||||
exploring how AI agents are revolutionizing personalized learning could captivate
|
||||
readers by detailing how these intelligent systems adapt educational content
|
||||
in real time to meet individual learner needs. By combining adaptive learning
|
||||
algorithms, natural language understanding, and behavioral analytics, AI agents
|
||||
are transforming classrooms and online platforms alike, providing personalized
|
||||
feedback, pacing, and even motivation strategies. This piece would dive into
|
||||
current implementations, such as AI tutors and automated grading, and forecast
|
||||
the potential for lifelong learning companions that evolve alongside the learner\u2019s
|
||||
growth. Readers would gain a comprehensive view of how AI agents can democratize
|
||||
access to education while enhancing efficacy across diverse learning environments.\\n\\n-
|
||||
**The Role of AI Agents in Accelerating Drug Discovery and Healthcare Innovation**
|
||||
\ \\nThis article would provide a compelling exploration of how AI agents are
|
||||
accelerating drug discovery by automating complex data analysis, predicting
|
||||
molecular interactions, and optimizing clinical trial design. It would highlight
|
||||
cutting-edge examples where AI agents collaborate with humans to speed up identifying
|
||||
promising compounds, reducing costs, and shortening development cycles. Beyond
|
||||
pharmaceuticals, the article could explore AI agents\u2019 expanding roles in
|
||||
personalized medicine, patient monitoring, and diagnostic support. With healthcare
|
||||
challenges mounting globally, this topic offers high relevance and excitement
|
||||
by showcasing how AI agents act as catalysts for breakthroughs that can save
|
||||
lives and transform medical care.\\n\\n- **Collaborative AI Agents: The Next
|
||||
Frontier in Software Development and DevOps** \\nAn in-depth article on collaborative
|
||||
AI agents in software engineering would showcase how these tools enhance productivity
|
||||
and code quality by automating routine tasks, catching bugs, and assisting in
|
||||
code reviews. It could examine emerging AI agents designed for pair programming,
|
||||
continuous integration, deployment automation, and intelligent testing. By integrating
|
||||
seamlessly into DevOps pipelines, these AI collaborators reduce human error,
|
||||
speed up delivery cycles, and enable developers to focus on innovation. The
|
||||
piece would also discuss challenges like trust, explainability, and maintaining
|
||||
human oversight, appealing to software engineers and technology leaders eager
|
||||
to understand the practical implications of AI agents in development workflows.\\n\\n-
|
||||
**AI Agents and the Evolution of Customer Experience in Digital Businesses**
|
||||
\ \\nThis article would explore the transformative role AI agents play in reshaping
|
||||
customer experience for digital-first businesses. It could cover AI-powered
|
||||
chatbots, recommendation engines, sentiment analysis tools, and personalized
|
||||
marketing agents, illustrating how these intelligent systems enhance engagement
|
||||
and satisfaction while reducing operational costs. By weaving in real-world
|
||||
case studies and data, the article would demonstrate how AI agents help companies
|
||||
anticipate customer needs, resolve issues proactively, and create seamless omnichannel
|
||||
interactions. The narrative could also touch on the balance between automation
|
||||
and human touch, offering strategic insights for businesses aiming to harness
|
||||
AI agents without compromising brand loyalty and trust.\\n\\n- **Ethical Frameworks
|
||||
for the Deployment of Autonomous AI Agents in Society** \\nThis article would
|
||||
address the critical and timely topic of ethical considerations surrounding
|
||||
the deployment of autonomous AI agents in public and private spheres. It would
|
||||
systematically analyze issues such as accountability, transparency, fairness,
|
||||
privacy, and unintended consequences when AI agents make decisions or act autonomously.
|
||||
The article could feature interviews with ethicists, policymakers, and researchers,
|
||||
and review current regulatory efforts and standards shaping AI governance. By
|
||||
unpacking this complex terrain, the article would provide a thoughtful, multidisciplinary
|
||||
perspective crucial for stakeholders aiming to responsibly develop and deploy
|
||||
AI agents in ways that align with societal values and legal frameworks.\\n\\nThis
|
||||
is the expected criteria for your final answer: Evaluation Score from 1 to 10
|
||||
based on the performance of the agents on the tasks\\nyou MUST return the actual
|
||||
complete content as the final answer, not a summary.\\nFormat your final answer
|
||||
according to the following OpenAPI schema: {\\n \\\"properties\\\": {\\n \\\"quality\\\":
|
||||
{\\n \\\"description\\\": \\\"A score from 1 to 10 evaluating on completion,
|
||||
quality, and overall performance from the task_description and task_expected_output
|
||||
to the actual Task Output.\\\",\\n \\\"title\\\": \\\"Quality\\\",\\n \\\"type\\\":
|
||||
\\\"number\\\"\\n }\\n },\\n \\\"required\\\": [\\n \\\"quality\\\"\\n
|
||||
\ ],\\n \\\"title\\\": \\\"TaskEvaluationPydanticOutput\\\",\\n \\\"type\\\":
|
||||
\\\"object\\\",\\n \\\"additionalProperties\\\": false\\n}\\n\\nIMPORTANT:
|
||||
Preserve the original content exactly as-is. Do NOT rewrite, paraphrase, or
|
||||
modify the meaning of the content. Only structure it to match the schema format.\\n\\nDo
|
||||
not include the OpenAPI schema in the final output. Ensure the final output
|
||||
does not include any code block markers like ```json or ```python.\\n\\nProvide
|
||||
your complete response:\"}],\"model\":\"gpt-4o-mini\",\"response_format\":{\"type\":\"json_schema\",\"json_schema\":{\"schema\":{\"properties\":{\"quality\":{\"description\":\"A
|
||||
score from 1 to 10 evaluating on completion, quality, and overall performance
|
||||
from the task_description and task_expected_output to the actual Task Output.\",\"title\":\"Quality\",\"type\":\"number\"}},\"required\":[\"quality\"],\"title\":\"TaskEvaluationPydanticOutput\",\"type\":\"object\",\"additionalProperties\":false},\"name\":\"TaskEvaluationPydanticOutput\",\"strict\":true}},\"stream\":false}"
|
||||
headers:
|
||||
@@ -674,7 +694,7 @@ interactions:
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '7196'
|
||||
- '7036'
|
||||
content-type:
|
||||
- application/json
|
||||
cookie:
|
||||
@@ -700,31 +720,33 @@ interactions:
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.13.12
|
||||
- 3.13.3
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: "{\n \"id\": \"chatcmpl-DDFzaYq2i96GKjZisy507Xk2rVvjn\",\n \"object\":
|
||||
\"chat.completion\",\n \"created\": 1772052110,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n
|
||||
string: "{\n \"id\": \"chatcmpl-DIjmAmRrx8ONo3OaHaBCNKzOa0Mzs\",\n \"object\":
|
||||
\"chat.completion\",\n \"created\": 1773358238,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n
|
||||
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||
\"assistant\",\n \"content\": \"{\\\"quality\\\":9}\",\n \"refusal\":
|
||||
null,\n \"annotations\": []\n },\n \"logprobs\": null,\n
|
||||
\ \"finish_reason\": \"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\":
|
||||
1264,\n \"completion_tokens\": 5,\n \"total_tokens\": 1269,\n \"prompt_tokens_details\":
|
||||
1214,\n \"completion_tokens\": 5,\n \"total_tokens\": 1219,\n \"prompt_tokens_details\":
|
||||
{\n \"cached_tokens\": 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\":
|
||||
{\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
||||
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\":
|
||||
\"default\",\n \"system_fingerprint\": \"fp_bd4be55b21\"\n}\n"
|
||||
\"default\",\n \"system_fingerprint\": \"fp_1d1f595505\"\n}\n"
|
||||
headers:
|
||||
CF-RAY:
|
||||
- CF-RAY-XXX
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-Ray:
|
||||
- 9db6967da94a10f3-EWR
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Wed, 25 Feb 2026 20:41:51 GMT
|
||||
- Thu, 12 Mar 2026 23:30:39 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Strict-Transport-Security:
|
||||
@@ -737,12 +759,10 @@ interactions:
|
||||
- ACCESS-CONTROL-XXX
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
cf-cache-status:
|
||||
- DYNAMIC
|
||||
openai-organization:
|
||||
- OPENAI-ORG-XXX
|
||||
openai-processing-ms:
|
||||
- '391'
|
||||
- '374'
|
||||
openai-project:
|
||||
- OPENAI-PROJECT-XXX
|
||||
openai-version:
|
||||
|
||||
@@ -1,111 +1,115 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: '{"messages": [{"role": "system", "content": "You are Researcher. You''re
|
||||
an expert researcher, specialized in technology, software engineering, AI and
|
||||
startups. You work as a freelancer and is now working on doing research and
|
||||
analysis for a new customer.\nYour personal goal is: Make the best research
|
||||
and analysis on content about AI and AI agents\nTo give my best complete final
|
||||
answer to the task use the exact following format:\n\nThought: I now can give
|
||||
a great answer\nFinal Answer: Your final answer must be the great and the most
|
||||
complete as possible, it must be outcome described.\n\nI MUST use these formats,
|
||||
my job depends on it!"}, {"role": "user", "content": "\nCurrent Task: Look at
|
||||
the available data and give me a sense on the total number of sales.\n\nThis
|
||||
is the expect criteria for your final answer: The total number of sales as an
|
||||
integer\nyou MUST return the actual complete content as the final answer, not
|
||||
a summary.\n\nBegin! This is VERY important to you, use the tools available
|
||||
and give your best Final Answer, your job depends on it!\n\nThought:"}], "model":
|
||||
"gpt-4o"}'
|
||||
body: '{"messages":[{"role":"system","content":"You are Researcher. You''re an
|
||||
expert researcher, specialized in technology, software engineering, AI and startups.
|
||||
You work as a freelancer and is now working on doing research and analysis for
|
||||
a new customer.\nYour personal goal is: Make the best research and analysis
|
||||
on content about AI and AI agents"},{"role":"user","content":"\nCurrent Task:
|
||||
Look at the available data and give me a sense on the total number of sales.\n\nThis
|
||||
is the expected criteria for your final answer: The total number of sales as
|
||||
an integer\nyou MUST return the actual complete content as the final answer,
|
||||
not a summary.\n\nProvide your complete response:"}],"model":"gpt-4.1-mini"}'
|
||||
headers:
|
||||
User-Agent:
|
||||
- X-USER-AGENT-XXX
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate
|
||||
- ACCEPT-ENCODING-XXX
|
||||
authorization:
|
||||
- AUTHORIZATION-XXX
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '1097'
|
||||
- '704'
|
||||
content-type:
|
||||
- application/json
|
||||
cookie:
|
||||
- __cf_bm=9.8sBYBkvBR8R1K_bVF7xgU..80XKlEIg3N2OBbTSCU-1727214102-1.0.1.1-.qiTLXbPamYUMSuyNsOEB9jhGu.jOifujOrx9E2JZvStbIZ9RTIiE44xKKNfLPxQkOi6qAT3h6htK8lPDGV_5g;
|
||||
_cfuvid=lbRdAddVWV6W3f5Dm9SaOPWDUOxqtZBSPr_fTW26nEA-1727213194587-0.0.1.1-604800000
|
||||
host:
|
||||
- api.openai.com
|
||||
user-agent:
|
||||
- OpenAI/Python 1.47.0
|
||||
x-stainless-arch:
|
||||
- arm64
|
||||
- X-STAINLESS-ARCH-XXX
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- MacOS
|
||||
- X-STAINLESS-OS-XXX
|
||||
x-stainless-package-version:
|
||||
- 1.47.0
|
||||
x-stainless-raw-response:
|
||||
- 'true'
|
||||
- 1.83.0
|
||||
x-stainless-read-timeout:
|
||||
- X-STAINLESS-READ-TIMEOUT-XXX
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.11.7
|
||||
- 3.13.3
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: "{\n \"id\": \"chatcmpl-AB7cBo2TPJMkfJCtCzpXOEixI8VrG\",\n \"object\"\
|
||||
: \"chat.completion\",\n \"created\": 1727214243,\n \"model\": \"gpt-4o-2024-05-13\"\
|
||||
,\n \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \
|
||||
\ \"role\": \"assistant\",\n \"content\": \"Thought: I need to\
|
||||
\ analyze the available data to determine the total number of sales accurately.\\\
|
||||
n\\nFinal Answer: The total number of sales is [the exact integer value of\
|
||||
\ the total sales from the given data].\",\n \"refusal\": null\n \
|
||||
\ },\n \"logprobs\": null,\n \"finish_reason\": \"stop\"\n \
|
||||
\ }\n ],\n \"usage\": {\n \"prompt_tokens\": 215,\n \"completion_tokens\"\
|
||||
: 41,\n \"total_tokens\": 256,\n \"completion_tokens_details\": {\n\
|
||||
\ \"reasoning_tokens\": 0\n }\n },\n \"system_fingerprint\": \"\
|
||||
fp_e375328146\"\n}\n"
|
||||
string: "{\n \"id\": \"chatcmpl-DIjv4bkJSathhHXsLANGZgGhV3rl7\",\n \"object\":
|
||||
\"chat.completion\",\n \"created\": 1773358790,\n \"model\": \"gpt-4.1-mini-2025-04-14\",\n
|
||||
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||
\"assistant\",\n \"content\": \"I don\u2019t see any data provided
|
||||
yet regarding sales figures. Please share the available data or provide additional
|
||||
details so I can analyze and calculate the total number of sales accurately.\",\n
|
||||
\ \"refusal\": null,\n \"annotations\": []\n },\n \"logprobs\":
|
||||
null,\n \"finish_reason\": \"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\":
|
||||
130,\n \"completion_tokens\": 34,\n \"total_tokens\": 164,\n \"prompt_tokens_details\":
|
||||
{\n \"cached_tokens\": 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\":
|
||||
{\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
||||
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\":
|
||||
\"default\",\n \"system_fingerprint\": \"fp_828130e5d4\"\n}\n"
|
||||
headers:
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-RAY:
|
||||
- 8c85f4176a8e1cf3-GRU
|
||||
CF-Ray:
|
||||
- 9db6a3f7ae211512-EWR
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Tue, 24 Sep 2024 21:44:03 GMT
|
||||
- Thu, 12 Mar 2026 23:39:51 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Strict-Transport-Security:
|
||||
- STS-XXX
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
- X-CONTENT-TYPE-XXX
|
||||
access-control-expose-headers:
|
||||
- X-Request-ID
|
||||
- ACCESS-CONTROL-XXX
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
openai-organization:
|
||||
- crewai-iuxna1
|
||||
- OPENAI-ORG-XXX
|
||||
openai-processing-ms:
|
||||
- '906'
|
||||
- '854'
|
||||
openai-project:
|
||||
- OPENAI-PROJECT-XXX
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
strict-transport-security:
|
||||
- max-age=31536000; includeSubDomains; preload
|
||||
set-cookie:
|
||||
- SET-COOKIE-XXX
|
||||
x-openai-proxy-wasm:
|
||||
- v0.1
|
||||
x-ratelimit-limit-requests:
|
||||
- '10000'
|
||||
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||
x-ratelimit-limit-tokens:
|
||||
- '30000000'
|
||||
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||
x-ratelimit-remaining-requests:
|
||||
- '9999'
|
||||
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||
x-ratelimit-remaining-tokens:
|
||||
- '29999735'
|
||||
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||
x-ratelimit-reset-requests:
|
||||
- 6ms
|
||||
- X-RATELIMIT-RESET-REQUESTS-XXX
|
||||
x-ratelimit-reset-tokens:
|
||||
- 0s
|
||||
- X-RATELIMIT-RESET-TOKENS-XXX
|
||||
x-request-id:
|
||||
- req_06bf7b348d3d142c9cb7cce4d956b8d6
|
||||
- X-REQUEST-ID-XXX
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,171 +1,118 @@
|
||||
interactions:
|
||||
- request:
|
||||
body: !!binary |
|
||||
CoEMCiQKIgoMc2VydmljZS5uYW1lEhIKEGNyZXdBSS10ZWxlbWV0cnkS2AsKEgoQY3Jld2FpLnRl
|
||||
bGVtZXRyeRKQAgoQ1lPH3Bis4hD4M7Sez2t96RIIIffb2kCAAqMqDlRhc2sgRXhlY3V0aW9uMAE5
|
||||
WIEZIiVM+BdBSK51sSZM+BdKLgoIY3Jld19rZXkSIgogM2Y4ZDVjM2FiODgyZDY4NjlkOTNjYjgx
|
||||
ZjBlMmVkNGFKMQoHY3Jld19pZBImCiQyYjZmY2ZmYS1lNDQ0LTQ4YjYtYWNjNi0xZTVhMDY2OTQ1
|
||||
NWJKLgoIdGFza19rZXkSIgogOTRhODI2YzE5MzA1NTk2ODZiYWZiNDA5ZWU4Mzg3NmZKMQoHdGFz
|
||||
a19pZBImCiQxMTU5NmU3OS0yYzllLTQzOWYtYWViMS0xMThhMTI2ZDNiYzN6AhgBhQEAAQAAEp0H
|
||||
ChBEYWf4sVuYMd8/Oxr4ONAsEghO/cKNNKdq0CoMQ3JldyBDcmVhdGVkMAE5KCBKsyZM+BdByI5P
|
||||
syZM+BdKGgoOY3Jld2FpX3ZlcnNpb24SCAoGMC42MS4wShoKDnB5dGhvbl92ZXJzaW9uEggKBjMu
|
||||
MTEuN0ouCghjcmV3X2tleRIiCiBhOWNjNWQ0MzM5NWIyMWIxODFjODBiZDQzNTFjY2VjOEoxCgdj
|
||||
cmV3X2lkEiYKJDkzNGJkMDZiLTY2ZDktNDE0MC1iZGE3LTQzMDZmNmM3Y2Q0N0ocCgxjcmV3X3By
|
||||
b2Nlc3MSDAoKc2VxdWVudGlhbEoRCgtjcmV3X21lbW9yeRICEABKGgoUY3Jld19udW1iZXJfb2Zf
|
||||
dGFza3MSAhgBShsKFWNyZXdfbnVtYmVyX29mX2FnZW50cxICGAFKzAIKC2NyZXdfYWdlbnRzErwC
|
||||
CrkCW3sia2V5IjogIjhiZDIxMzliNTk3NTE4MTUwNmU0MWZkOWM0NTYzZDc1IiwgImlkIjogIjY3
|
||||
MWMzYzdmLWNjMzUtNGU5MS1hYjgzLWVmZGVjOWU3Y2ZiNyIsICJyb2xlIjogIlJlc2VhcmNoZXIi
|
||||
LCAidmVyYm9zZT8iOiBmYWxzZSwgIm1heF9pdGVyIjogMTUsICJtYXhfcnBtIjogbnVsbCwgImZ1
|
||||
bmN0aW9uX2NhbGxpbmdfbGxtIjogIiIsICJsbG0iOiAiZ3B0LTRvIiwgImRlbGVnYXRpb25fZW5h
|
||||
YmxlZD8iOiBmYWxzZSwgImFsbG93X2NvZGVfZXhlY3V0aW9uPyI6IGZhbHNlLCAibWF4X3JldHJ5
|
||||
X2xpbWl0IjogMiwgInRvb2xzX25hbWVzIjogW119XUr+AQoKY3Jld190YXNrcxLvAQrsAVt7Imtl
|
||||
eSI6ICJlOWU2YjcyYWFjMzI2NDU5ZGQ3MDY4ZjBiMTcxN2MxYyIsICJpZCI6ICI4YmFkNTJiZi05
|
||||
MGM0LTQ0ZDgtYmNlZi0xODBkZTA2MjRiYWYiLCAiYXN5bmNfZXhlY3V0aW9uPyI6IHRydWUsICJo
|
||||
dW1hbl9pbnB1dD8iOiBmYWxzZSwgImFnZW50X3JvbGUiOiAiUmVzZWFyY2hlciIsICJhZ2VudF9r
|
||||
ZXkiOiAiOGJkMjEzOWI1OTc1MTgxNTA2ZTQxZmQ5YzQ1NjNkNzUiLCAidG9vbHNfbmFtZXMiOiBb
|
||||
XX1degIYAYUBAAEAABKOAgoQduJhIxVspIn9gWgZzmXHrhIILYsCkB2V4ckqDFRhc2sgQ3JlYXRl
|
||||
ZDABORCOYrMmTPgXQdDrYrMmTPgXSi4KCGNyZXdfa2V5EiIKIGE5Y2M1ZDQzMzk1YjIxYjE4MWM4
|
||||
MGJkNDM1MWNjZWM4SjEKB2NyZXdfaWQSJgokOTM0YmQwNmItNjZkOS00MTQwLWJkYTctNDMwNmY2
|
||||
YzdjZDQ3Si4KCHRhc2tfa2V5EiIKIGU5ZTZiNzJhYWMzMjY0NTlkZDcwNjhmMGIxNzE3YzFjSjEK
|
||||
B3Rhc2tfaWQSJgokOGJhZDUyYmYtOTBjNC00NGQ4LWJjZWYtMTgwZGUwNjI0YmFmegIYAYUBAAEA
|
||||
AA==
|
||||
body: '{"messages":[{"role":"system","content":"You are Researcher. You''re an
|
||||
expert researcher, specialized in technology, software engineering, AI and startups.
|
||||
You work as a freelancer and is now working on doing research and analysis for
|
||||
a new customer.\nYour personal goal is: Make the best research and analysis
|
||||
on content about AI and AI agents"},{"role":"user","content":"\nCurrent Task:
|
||||
Generate a list of 5 interesting ideas to explore for an article, where each
|
||||
bulletpoint is under 15 words.\n\nThis is the expected criteria for your final
|
||||
answer: Bullet point list of 5 important events. No additional commentary.\nyou
|
||||
MUST return the actual complete content as the final answer, not a summary.\n\nProvide
|
||||
your complete response:"}],"model":"gpt-4.1-mini"}'
|
||||
headers:
|
||||
Accept:
|
||||
- '*/*'
|
||||
Accept-Encoding:
|
||||
- gzip, deflate
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Length:
|
||||
- '1540'
|
||||
Content-Type:
|
||||
- application/x-protobuf
|
||||
User-Agent:
|
||||
- OTel-OTLP-Exporter-Python/1.27.0
|
||||
method: POST
|
||||
uri: https://telemetry.crewai.com:4319/v1/traces
|
||||
response:
|
||||
body:
|
||||
string: "\n\0"
|
||||
headers:
|
||||
Content-Length:
|
||||
- '2'
|
||||
Content-Type:
|
||||
- application/x-protobuf
|
||||
Date:
|
||||
- Tue, 24 Sep 2024 21:43:06 GMT
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
- request:
|
||||
body: '{"messages": [{"role": "system", "content": "You are Researcher. You''re
|
||||
an expert researcher, specialized in technology, software engineering, AI and
|
||||
startups. You work as a freelancer and is now working on doing research and
|
||||
analysis for a new customer.\nYour personal goal is: Make the best research
|
||||
and analysis on content about AI and AI agents\nTo give my best complete final
|
||||
answer to the task use the exact following format:\n\nThought: I now can give
|
||||
a great answer\nFinal Answer: Your final answer must be the great and the most
|
||||
complete as possible, it must be outcome described.\n\nI MUST use these formats,
|
||||
my job depends on it!"}, {"role": "user", "content": "\nCurrent Task: Generate
|
||||
a list of 5 interesting ideas to explore for an article, where each bulletpoint
|
||||
is under 15 words.\n\nThis is the expect criteria for your final answer: Bullet
|
||||
point list of 5 important events. No additional commentary.\nyou MUST return
|
||||
the actual complete content as the final answer, not a summary.\n\nBegin! This
|
||||
is VERY important to you, use the tools available and give your best Final Answer,
|
||||
your job depends on it!\n\nThought:"}], "model": "gpt-4o"}'
|
||||
headers:
|
||||
- X-USER-AGENT-XXX
|
||||
accept:
|
||||
- application/json
|
||||
accept-encoding:
|
||||
- gzip, deflate
|
||||
- ACCEPT-ENCODING-XXX
|
||||
authorization:
|
||||
- AUTHORIZATION-XXX
|
||||
connection:
|
||||
- keep-alive
|
||||
content-length:
|
||||
- '1155'
|
||||
- '762'
|
||||
content-type:
|
||||
- application/json
|
||||
cookie:
|
||||
- __cf_bm=9.8sBYBkvBR8R1K_bVF7xgU..80XKlEIg3N2OBbTSCU-1727214102-1.0.1.1-.qiTLXbPamYUMSuyNsOEB9jhGu.jOifujOrx9E2JZvStbIZ9RTIiE44xKKNfLPxQkOi6qAT3h6htK8lPDGV_5g;
|
||||
_cfuvid=lbRdAddVWV6W3f5Dm9SaOPWDUOxqtZBSPr_fTW26nEA-1727213194587-0.0.1.1-604800000
|
||||
host:
|
||||
- api.openai.com
|
||||
user-agent:
|
||||
- OpenAI/Python 1.47.0
|
||||
x-stainless-arch:
|
||||
- arm64
|
||||
- X-STAINLESS-ARCH-XXX
|
||||
x-stainless-async:
|
||||
- 'false'
|
||||
x-stainless-lang:
|
||||
- python
|
||||
x-stainless-os:
|
||||
- MacOS
|
||||
- X-STAINLESS-OS-XXX
|
||||
x-stainless-package-version:
|
||||
- 1.47.0
|
||||
x-stainless-raw-response:
|
||||
- 'true'
|
||||
- 1.83.0
|
||||
x-stainless-read-timeout:
|
||||
- X-STAINLESS-READ-TIMEOUT-XXX
|
||||
x-stainless-retry-count:
|
||||
- '0'
|
||||
x-stainless-runtime:
|
||||
- CPython
|
||||
x-stainless-runtime-version:
|
||||
- 3.11.7
|
||||
- 3.13.3
|
||||
method: POST
|
||||
uri: https://api.openai.com/v1/chat/completions
|
||||
response:
|
||||
body:
|
||||
string: "{\n \"id\": \"chatcmpl-AB7bGdQd8mh4zvM4UaLl93hex1Ys3\",\n \"object\"\
|
||||
: \"chat.completion\",\n \"created\": 1727214186,\n \"model\": \"gpt-4o-2024-05-13\"\
|
||||
,\n \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \
|
||||
\ \"role\": \"assistant\",\n \"content\": \"Thought: I now can\
|
||||
\ give a great answer.\\nFinal Answer:\\n- Ethical implications of AI in law\
|
||||
\ enforcement and surveillance.\\n- AI advancements in personalized healthcare\
|
||||
\ and diagnostics.\\n- Autonomous AI agents in financial market trading.\\\
|
||||
n- Collaboration between AI and humans in creative arts.\\n- AI-driven climate\
|
||||
\ modeling and environmental monitoring.\",\n \"refusal\": null\n \
|
||||
\ },\n \"logprobs\": null,\n \"finish_reason\": \"stop\"\n \
|
||||
\ }\n ],\n \"usage\": {\n \"prompt_tokens\": 226,\n \"completion_tokens\"\
|
||||
: 61,\n \"total_tokens\": 287,\n \"completion_tokens_details\": {\n\
|
||||
\ \"reasoning_tokens\": 0\n }\n },\n \"system_fingerprint\": \"\
|
||||
fp_e375328146\"\n}\n"
|
||||
string: "{\n \"id\": \"chatcmpl-DIkLSp6YfhftRnhYHqjRHZXAI8Sji\",\n \"object\":
|
||||
\"chat.completion\",\n \"created\": 1773360426,\n \"model\": \"gpt-4.1-mini-2025-04-14\",\n
|
||||
\ \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\":
|
||||
\"assistant\",\n \"content\": \"- Impact of autonomous AI agents on
|
||||
future workplace automation \\n- Ethical dilemmas in deploying AI decision-making
|
||||
systems \\n- Advances in AI-driven personalized learning technologies \\n-
|
||||
Role of AI in enhancing cybersecurity defense mechanisms \\n- Challenges
|
||||
in regulating AI innovations across global markets\",\n \"refusal\":
|
||||
null,\n \"annotations\": []\n },\n \"logprobs\": null,\n
|
||||
\ \"finish_reason\": \"stop\"\n }\n ],\n \"usage\": {\n \"prompt_tokens\":
|
||||
141,\n \"completion_tokens\": 50,\n \"total_tokens\": 191,\n \"prompt_tokens_details\":
|
||||
{\n \"cached_tokens\": 0,\n \"audio_tokens\": 0\n },\n \"completion_tokens_details\":
|
||||
{\n \"reasoning_tokens\": 0,\n \"audio_tokens\": 0,\n \"accepted_prediction_tokens\":
|
||||
0,\n \"rejected_prediction_tokens\": 0\n }\n },\n \"service_tier\":
|
||||
\"default\",\n \"system_fingerprint\": \"fp_5e793402c9\"\n}\n"
|
||||
headers:
|
||||
CF-Cache-Status:
|
||||
- DYNAMIC
|
||||
CF-RAY:
|
||||
- 8c85f2b7c92f1cf3-GRU
|
||||
CF-Ray:
|
||||
- 9db6cbea1bd29d36-EWR
|
||||
Connection:
|
||||
- keep-alive
|
||||
Content-Type:
|
||||
- application/json
|
||||
Date:
|
||||
- Tue, 24 Sep 2024 21:43:07 GMT
|
||||
- Fri, 13 Mar 2026 00:07:08 GMT
|
||||
Server:
|
||||
- cloudflare
|
||||
Strict-Transport-Security:
|
||||
- STS-XXX
|
||||
Transfer-Encoding:
|
||||
- chunked
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
- X-CONTENT-TYPE-XXX
|
||||
access-control-expose-headers:
|
||||
- X-Request-ID
|
||||
- ACCESS-CONTROL-XXX
|
||||
alt-svc:
|
||||
- h3=":443"; ma=86400
|
||||
openai-organization:
|
||||
- crewai-iuxna1
|
||||
- OPENAI-ORG-XXX
|
||||
openai-processing-ms:
|
||||
- '939'
|
||||
- '1357'
|
||||
openai-project:
|
||||
- OPENAI-PROJECT-XXX
|
||||
openai-version:
|
||||
- '2020-10-01'
|
||||
strict-transport-security:
|
||||
- max-age=31536000; includeSubDomains; preload
|
||||
set-cookie:
|
||||
- SET-COOKIE-XXX
|
||||
x-openai-proxy-wasm:
|
||||
- v0.1
|
||||
x-ratelimit-limit-requests:
|
||||
- '10000'
|
||||
- X-RATELIMIT-LIMIT-REQUESTS-XXX
|
||||
x-ratelimit-limit-tokens:
|
||||
- '30000000'
|
||||
- X-RATELIMIT-LIMIT-TOKENS-XXX
|
||||
x-ratelimit-remaining-requests:
|
||||
- '9999'
|
||||
- X-RATELIMIT-REMAINING-REQUESTS-XXX
|
||||
x-ratelimit-remaining-tokens:
|
||||
- '29999722'
|
||||
- X-RATELIMIT-REMAINING-TOKENS-XXX
|
||||
x-ratelimit-reset-requests:
|
||||
- 6ms
|
||||
- X-RATELIMIT-RESET-REQUESTS-XXX
|
||||
x-ratelimit-reset-tokens:
|
||||
- 0s
|
||||
- X-RATELIMIT-RESET-TOKENS-XXX
|
||||
x-request-id:
|
||||
- req_4a6962cfb5b3418a75c19cfc1c2e7227
|
||||
- X-REQUEST-ID-XXX
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
|
||||
@@ -1101,7 +1101,7 @@ def test_single_task_with_async_execution():
|
||||
|
||||
result = crew.kickoff()
|
||||
assert result.raw.startswith(
|
||||
"- Ethical implications of AI in law enforcement and surveillance."
|
||||
"- Impact of autonomous AI agents on future workplace automation"
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -23,15 +23,9 @@ class TestTraceListenerSetup:
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_user_data_file_io(self):
|
||||
"""Mock user data file I/O to prevent file system pollution between tests"""
|
||||
with (
|
||||
patch(
|
||||
"crewai.events.listeners.tracing.utils._load_user_data",
|
||||
return_value={},
|
||||
),
|
||||
patch(
|
||||
"crewai.events.listeners.tracing.utils._save_user_data",
|
||||
return_value=None,
|
||||
),
|
||||
with patch(
|
||||
"crewai.events.listeners.tracing.utils._load_user_data",
|
||||
return_value={},
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
Reference in New Issue
Block a user