fix regressions and bunch of issues
This commit is contained in:
parent
e75c483479
commit
f7cf33c610
254 changed files with 9072 additions and 2918584 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -28,3 +28,7 @@ nltk_data/
|
|||
logs/
|
||||
|
||||
.DS_Store
|
||||
|
||||
|
||||
hindsight-dev/benchmarks/locomo/results/
|
||||
hindsight-dev/benchmarks/longmemeval/results/
|
||||
128
RELEASE.md
128
RELEASE.md
|
|
@ -1,128 +0,0 @@
|
|||
# Release Guide
|
||||
|
||||
## Release Process
|
||||
|
||||
### 1. Generate OpenAPI Spec
|
||||
|
||||
```bash
|
||||
uv sync
|
||||
cd hindsight-dev
|
||||
uv run generate-openapi
|
||||
cd ..
|
||||
```
|
||||
|
||||
### 2. Generate API Clients
|
||||
|
||||
```bash
|
||||
./scripts/generate-clients.sh
|
||||
```
|
||||
|
||||
This regenerates Python and TypeScript clients from `openapi.json`.
|
||||
|
||||
**Note:** Your `pyproject.toml` and `package.json` are preserved - only code is regenerated.
|
||||
|
||||
### 3. Commit Everything
|
||||
|
||||
```bash
|
||||
git add openapi.json hindsight-clients/
|
||||
git commit -m "Update OpenAPI spec and regenerate clients"
|
||||
```
|
||||
|
||||
### 4. Run Release Script
|
||||
|
||||
```bash
|
||||
./scripts/release.sh 0.0.6
|
||||
```
|
||||
|
||||
This will:
|
||||
- Update version to `0.0.6` in **all** components (core, clients, CLI, UI, Helm)
|
||||
- Commit changes
|
||||
- Create and push tag `v0.0.6`
|
||||
- Trigger GitHub Actions (builds Python package, Rust CLI, Docker images, Helm chart)
|
||||
|
||||
---
|
||||
|
||||
## After GitHub Actions Complete
|
||||
|
||||
### Publish Python Client to PyPI
|
||||
|
||||
```bash
|
||||
cd hindsight-clients/python
|
||||
uv build
|
||||
uv publish
|
||||
```
|
||||
|
||||
### Publish TypeScript Client to NPM
|
||||
|
||||
```bash
|
||||
cd hindsight-clients/typescript
|
||||
npm install
|
||||
npm run build
|
||||
npm publish --access public
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pre-Release Checklist
|
||||
|
||||
- [ ] Tests passing: `cd hindsight-api && uv run pytest tests`
|
||||
- [ ] No uncommitted changes: `git status`
|
||||
- [ ] On `main` branch
|
||||
|
||||
---
|
||||
|
||||
## Versioning
|
||||
|
||||
**Semantic Versioning: `MAJOR.MINOR.PATCH`**
|
||||
|
||||
- **PATCH** (0.0.6): Bug fixes, no API changes
|
||||
- **MINOR** (0.1.0): New features, backward compatible
|
||||
- **MAJOR** (1.0.0): Breaking changes
|
||||
|
||||
**All components use the same version** - coordinated releases for simplicity.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Tag already exists:**
|
||||
```bash
|
||||
git tag -d v0.0.6
|
||||
git push origin :refs/tags/v0.0.6
|
||||
```
|
||||
|
||||
**Working directory not clean:**
|
||||
```bash
|
||||
git status
|
||||
# Commit or stash changes first
|
||||
```
|
||||
|
||||
**GitHub Actions failed:**
|
||||
- Check: https://github.com/vectorize-io/hindsight/actions
|
||||
- Re-run failed jobs or fix and release new patch version
|
||||
|
||||
**Rollback:**
|
||||
```bash
|
||||
git tag -d v0.0.6
|
||||
git push origin :refs/tags/v0.0.6
|
||||
git revert HEAD
|
||||
git push
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
```bash
|
||||
# Full release workflow
|
||||
uv sync
|
||||
cd hindsight-dev && uv run generate-openapi && cd ..
|
||||
./scripts/generate-clients.sh
|
||||
git add openapi.json hindsight-clients/
|
||||
git commit -m "Update OpenAPI spec and regenerate clients"
|
||||
./scripts/release.sh 0.0.6
|
||||
|
||||
# After GH Actions complete:
|
||||
cd hindsight-clients/python && uv build && uv publish
|
||||
cd ../typescript && npm run build && npm publish --access public
|
||||
```
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/bash
|
||||
# Rebuild Hindsight images from scratch
|
||||
|
||||
cd "$(dirname "$0")/standalone"
|
||||
|
||||
echo "🔨 Rebuilding Hindsight images (no cache)..."
|
||||
echo ""
|
||||
|
||||
# Build with no cache to force complete rebuild
|
||||
docker-compose build --no-cache
|
||||
|
||||
echo ""
|
||||
echo "✅ Rebuild complete!"
|
||||
echo ""
|
||||
echo "To start Hindsight:"
|
||||
echo " ./start.sh"
|
||||
|
|
@ -99,6 +99,11 @@ RUN chmod +x /app/start-all.sh
|
|||
# Create data directory for pg0
|
||||
RUN mkdir -p /app/data
|
||||
|
||||
# Install pg0 to /root/.hindsight/bin/pg0
|
||||
RUN mkdir -p /root/.hindsight/bin /root/.local/bin && \
|
||||
export PATH="/root/.hindsight/bin:/root/.local/bin:$PATH" && \
|
||||
curl -fsSL https://raw.githubusercontent.com/vectorize-io/pg0/main/install.sh | bash
|
||||
|
||||
# Expose ports
|
||||
EXPOSE 8888 3000
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ services:
|
|||
build:
|
||||
context: ../..
|
||||
dockerfile: docker/standalone/Dockerfile
|
||||
platforms:
|
||||
- linux/amd64
|
||||
platform: linux/amd64
|
||||
ports:
|
||||
- "3000:3000"
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ done
|
|||
# Start Control Plane
|
||||
echo "🎛️ Starting Control Plane..."
|
||||
cd /app/control-plane
|
||||
npm start &
|
||||
node .next/standalone/server.js &
|
||||
CP_PID=$!
|
||||
|
||||
echo ""
|
||||
|
|
|
|||
|
|
@ -350,10 +350,6 @@ class ReflectIncludeOptions(BaseModel):
|
|||
default=None,
|
||||
description="Include facts that the answer is based on. Set to {} to enable, null to disable (default: disabled)."
|
||||
)
|
||||
entities: Optional[EntityIncludeOptions] = Field(
|
||||
default=None,
|
||||
description="Include entity observations. Set to {max_tokens: N} to enable, null to disable (default: disabled)."
|
||||
)
|
||||
|
||||
|
||||
class ReflectRequest(BaseModel):
|
||||
|
|
@ -365,8 +361,7 @@ class ReflectRequest(BaseModel):
|
|||
"context": "This is for a research paper on AI ethics",
|
||||
"filters": [{"key": "source", "value": "slack", "match_unset": True}],
|
||||
"include": {
|
||||
"facts": {},
|
||||
"entities": {"max_tokens": 500}
|
||||
"facts": {}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
@ -375,7 +370,7 @@ class ReflectRequest(BaseModel):
|
|||
budget: Budget = Budget.LOW
|
||||
context: Optional[str] = None
|
||||
filters: Optional[List[MetadataFilter]] = Field(default=None, description="Filter by metadata. Multiple filters are ANDed together.")
|
||||
include: ReflectIncludeOptions = Field(default_factory=ReflectIncludeOptions, description="Options for including additional data (both disabled by default)")
|
||||
include: ReflectIncludeOptions = Field(default_factory=ReflectIncludeOptions, description="Options for including additional data (disabled by default)")
|
||||
|
||||
|
||||
class OpinionItem(BaseModel):
|
||||
|
|
@ -1026,12 +1021,6 @@ def _register_routes(app: FastAPI):
|
|||
occurred_end=fact.occurred_end
|
||||
))
|
||||
|
||||
# TODO: Handle entities inclusion when supported in reflect
|
||||
# entities_response = None
|
||||
# if request.include.entities is not None:
|
||||
# max_entity_tokens = request.include.entities.max_tokens
|
||||
# # ... fetch and format entities
|
||||
|
||||
return ReflectResponse(
|
||||
text=core_result.text,
|
||||
based_on=based_on_facts,
|
||||
|
|
@ -1437,10 +1426,10 @@ This operation cannot be undone.
|
|||
async with acquire_with_retry(pool) as conn:
|
||||
operations = await conn.fetch(
|
||||
"""
|
||||
SELECT id, bank_id, task_type, items_count, document_id, created_at, status, error_message
|
||||
SELECT operation_id, bank_id, operation_type, created_at, status, error_message, result_metadata
|
||||
FROM async_operations
|
||||
WHERE bank_id = $1
|
||||
ORDER BY created_at ASC
|
||||
ORDER BY created_at DESC
|
||||
""",
|
||||
bank_id
|
||||
)
|
||||
|
|
@ -1449,10 +1438,10 @@ This operation cannot be undone.
|
|||
"bank_id": bank_id,
|
||||
"operations": [
|
||||
{
|
||||
"id": str(row['id']),
|
||||
"task_type": row['task_type'],
|
||||
"items_count": row['items_count'],
|
||||
"document_id": row['document_id'],
|
||||
"id": str(row['operation_id']),
|
||||
"task_type": row['operation_type'],
|
||||
"items_count": row['result_metadata'].get('items_count', 0) if row['result_metadata'] else 0,
|
||||
"document_id": row['result_metadata'].get('document_id') if row['result_metadata'] else None,
|
||||
"created_at": row['created_at'].isoformat(),
|
||||
"status": row['status'],
|
||||
"error_message": row['error_message']
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ class SentenceTransformersEmbeddings(Embeddings):
|
|||
"""
|
||||
self.model_name = model_name
|
||||
self._model = None
|
||||
self._load_model()
|
||||
|
||||
def _load_model(self):
|
||||
"""Lazy load and validate the SentenceTransformer model."""
|
||||
|
|
@ -96,6 +97,5 @@ class SentenceTransformersEmbeddings(Embeddings):
|
|||
Returns:
|
||||
List of 384-dimensional embedding vectors
|
||||
"""
|
||||
self._load_model()
|
||||
embeddings = self._model.encode(texts, convert_to_numpy=True, show_progress_bar=False)
|
||||
return [emb.tolist() for emb in embeddings]
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ class LLMConfig:
|
|||
messages: List[Dict[str, str]],
|
||||
response_format: Optional[Any] = None,
|
||||
scope: str = "memory",
|
||||
max_retries: int = 5,
|
||||
max_retries: int = 10,
|
||||
initial_backoff: float = 1.0,
|
||||
max_backoff: float = 60.0,
|
||||
skip_validation: bool = False,
|
||||
|
|
|
|||
|
|
@ -1583,6 +1583,10 @@ class MemoryEngine:
|
|||
# Delete all data for the bank
|
||||
units_count = await conn.fetchval("SELECT COUNT(*) FROM memory_units WHERE bank_id = $1", bank_id)
|
||||
entities_count = await conn.fetchval("SELECT COUNT(*) FROM entities WHERE bank_id = $1", bank_id)
|
||||
documents_count = await conn.fetchval("SELECT COUNT(*) FROM documents WHERE bank_id = $1", bank_id)
|
||||
|
||||
# Delete documents (cascades to chunks)
|
||||
await conn.execute("DELETE FROM documents WHERE bank_id = $1", bank_id)
|
||||
|
||||
# Delete memory units (cascades to unit_entities, memory_links)
|
||||
await conn.execute("DELETE FROM memory_units WHERE bank_id = $1", bank_id)
|
||||
|
|
@ -1592,7 +1596,8 @@ class MemoryEngine:
|
|||
|
||||
return {
|
||||
"memory_units_deleted": units_count,
|
||||
"entities_deleted": entities_count
|
||||
"entities_deleted": entities_count,
|
||||
"documents_deleted": documents_count
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
|
|
@ -1629,7 +1634,7 @@ class MemoryEngine:
|
|||
where_clause = "WHERE " + " AND ".join(query_conditions) if query_conditions else ""
|
||||
|
||||
units = await conn.fetch(f"""
|
||||
SELECT id, text, event_date, context, occurred_start, occurred_end, mentioned_at
|
||||
SELECT id, text, event_date, context, occurred_start, occurred_end, mentioned_at, document_id
|
||||
FROM memory_units
|
||||
{where_clause}
|
||||
ORDER BY mentioned_at DESC NULLS LAST, event_date DESC
|
||||
|
|
@ -1745,14 +1750,15 @@ class MemoryEngine:
|
|||
entities = entity_map.get(unit_id, [])
|
||||
|
||||
table_rows.append({
|
||||
"id": str(unit_id)[:8] + "...",
|
||||
"id": str(unit_id),
|
||||
"text": row['text'],
|
||||
"context": row['context'] if row['context'] else "N/A",
|
||||
"occurred_start": row['occurred_start'].isoformat() if row['occurred_start'] else None,
|
||||
"occurred_end": row['occurred_end'].isoformat() if row['occurred_end'] else None,
|
||||
"mentioned_at": row['mentioned_at'].isoformat() if row['mentioned_at'] else None,
|
||||
"date": row['event_date'].strftime("%Y-%m-%d %H:%M") if row['event_date'] else "N/A", # Deprecated, kept for backwards compatibility
|
||||
"entities": ", ".join(entities) if entities else "None"
|
||||
"entities": ", ".join(entities) if entities else "None",
|
||||
"document_id": row['document_id']
|
||||
})
|
||||
|
||||
return {
|
||||
|
|
@ -1827,7 +1833,7 @@ class MemoryEngine:
|
|||
query_params.append(offset)
|
||||
|
||||
units = await conn.fetch(f"""
|
||||
SELECT id, text, event_date, context, fact_type
|
||||
SELECT id, text, event_date, context, fact_type, mentioned_at, occurred_start, occurred_end
|
||||
FROM memory_units
|
||||
{where_clause}
|
||||
ORDER BY mentioned_at DESC NULLS LAST, created_at DESC
|
||||
|
|
@ -1868,6 +1874,9 @@ class MemoryEngine:
|
|||
"context": row['context'] if row['context'] else "",
|
||||
"date": row['event_date'].isoformat() if row['event_date'] else "",
|
||||
"fact_type": row['fact_type'],
|
||||
"mentioned_at": row['mentioned_at'].isoformat() if row['mentioned_at'] else None,
|
||||
"occurred_start": row['occurred_start'].isoformat() if row['occurred_start'] else None,
|
||||
"occurred_end": row['occurred_end'].isoformat() if row['occurred_end'] else None,
|
||||
"entities": ", ".join(entities) if entities else ""
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -41,8 +41,9 @@ async def check_duplicates_batch(
|
|||
# Group facts by event_date (rounded to 12-hour buckets) for efficient batching
|
||||
time_buckets = defaultdict(list)
|
||||
for idx, fact in enumerate(facts):
|
||||
# Use occurred_start as the representative date
|
||||
fact_date = fact.occurred_start
|
||||
# Use occurred_start if available, otherwise use mentioned_at
|
||||
# For deduplication purposes, we need a time reference
|
||||
fact_date = fact.occurred_start if fact.occurred_start is not None else fact.mentioned_at
|
||||
# Round to 12-hour bucket to group similar times
|
||||
bucket_key = fact_date.replace(
|
||||
hour=(fact_date.hour // 12) * 12,
|
||||
|
|
|
|||
|
|
@ -47,8 +47,13 @@ async def process_entities_batch(
|
|||
|
||||
# Extract data for link_utils function
|
||||
fact_texts = [fact.fact_text for fact in facts]
|
||||
fact_dates = [fact.occurred_start for fact in facts]
|
||||
entities_per_fact = [[entity.name for entity in (fact.entities or [])] for fact in facts]
|
||||
# Use occurred_start if available, otherwise use mentioned_at for entity timestamps
|
||||
fact_dates = [fact.occurred_start if fact.occurred_start is not None else fact.mentioned_at for fact in facts]
|
||||
# Convert EntityRef objects to dict format expected by link_utils
|
||||
entities_per_fact = [
|
||||
[{'text': entity.name, 'type': 'CONCEPT'} for entity in (fact.entities or [])]
|
||||
for fact in facts
|
||||
]
|
||||
|
||||
# Use existing link_utils function for entity processing
|
||||
entity_links = await link_utils.extract_entities_batch_optimized(
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ class ExtractedFact(BaseModel):
|
|||
)
|
||||
observations: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Observations and inferences as a COMPLETE SENTENCE. Include subject + observed/inferred fact. Examples: 'Calvin traveled to Miami for the shoot', 'Gina won dance trophies in competitions', 'She knows programming from previous projects'"
|
||||
description="Observations, inferences, and specific details/metrics as a COMPLETE SENTENCE. Include subject + observed fact. Use this to capture: background facts, achievements, metrics, personal records, skills. Examples: 'Calvin traveled to Miami for the shoot', 'Gina won dance trophies in competitions', 'She knows programming from previous projects', 'User's personal best 5K time is 25:50', 'Sarah has completed 15 marathons', 'He speaks three languages fluently'"
|
||||
)
|
||||
|
||||
# Fact kind - optional hint for LLM thinking, not critical for extraction
|
||||
|
|
@ -122,11 +122,11 @@ class ExtractedFact(BaseModel):
|
|||
# Temporal fields - optional
|
||||
occurred_start: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Optional: ISO format timestamp for when event started. Only needed for specific events."
|
||||
description="WHEN THE EVENT ACTUALLY HAPPENED (not when mentioned). ISO timestamp. For datable events only (fact_kind='event'). Examples: 'went to Tokyo last spring' on June 10 → occurred_start='2024-03-01' (spring start), 'accident yesterday' on March 15 → occurred_start='2024-03-14' (yesterday). Leave null for general info (fact_kind='conversation')."
|
||||
)
|
||||
occurred_end: Optional[str] = Field(
|
||||
default=None,
|
||||
description="Optional: ISO format timestamp for when event ended. Only needed for specific events."
|
||||
description="WHEN THE EVENT ACTUALLY ENDED (not when mentioned). ISO timestamp. For datable events with duration (fact_kind='event'). Examples: 'went to Tokyo last spring' → occurred_end='2024-05-31' (spring end). Can be same as occurred_start for single-day events. Leave null for general info."
|
||||
)
|
||||
|
||||
# Classification (CRITICAL - required)
|
||||
|
|
@ -261,294 +261,142 @@ async def _extract_facts_from_chunk(
|
|||
else:
|
||||
fact_types_instruction = "Extract ONLY 'world' and 'assistant' type facts. DO NOT extract opinions - those are extracted separately."
|
||||
|
||||
prompt = f"""You are extracting comprehensive, narrative facts from conversations/document for an AI memory system.
|
||||
prompt = f"""Extract comprehensive facts from user text for an AI memory system.
|
||||
|
||||
{fact_types_instruction}
|
||||
|
||||
## CONTEXT INFORMATION
|
||||
- Context: {context if context else 'no additional context provided'}{agent_context}
|
||||
## CONTEXT
|
||||
- Context: {context if context else 'none'}{agent_context}
|
||||
|
||||
**TEMPORAL EXTRACTION **:
|
||||
- **occurred_start/end** (OPTIONAL): Only extract these for specific events mentioned within the conversation
|
||||
- Example: "I'm hosting a party next month" - extract when the party will happen (resolve to absolute dates using the reference date)
|
||||
- Leave empty if no specific event timing is mentioned
|
||||
- Use the reference date (event_date) to resolve relative time expressions to absolute ISO timestamps
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
SECTION 1: TEMPORAL HANDLING (CRITICAL)
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
## CORE PRINCIPLE: Extract ALL Meaningful Information Efficiently
|
||||
### 1.1 DETECT TEMPORAL MARKERS
|
||||
Watch for: "yesterday", "last week/month/year/summer", "ago", "tomorrow", "next", "happened", "occurred", past tense verbs ("went", "visited", "saw")
|
||||
|
||||
**GOAL**: Capture ALL meaningful information, but combine related exchanges efficiently. Don't create separate facts for questions - merge Q&A into single facts.
|
||||
### 1.2 DUAL FACT CREATION (KEY RULE)
|
||||
When text mentions a past/future event → Create TWO facts:
|
||||
1. MENTION FACT: "On [context date], it was mentioned that..." (occurred_start = context date)
|
||||
2. EVENT FACT: "[Action] in [absolute date]" (occurred_start = actual event date)
|
||||
|
||||
Each fact should:
|
||||
1. **CAPTURE ALL MEANINGFUL CONTENT** - Activities, projects, preferences, recommendations, encouragement WITH specific content
|
||||
2. **BE SELF-CONTAINED** - Readable without the original text
|
||||
3. **PRESERVE SPECIFIC CONTENT** - Capture WHAT was said, not just THAT something was said
|
||||
4. **COMBINE Q&A** - A question and its answer = ONE fact, not two separate facts
|
||||
### 1.3 ABSOLUTE DATE CONVERSION
|
||||
ALWAYS convert relative → absolute in factual_core text:
|
||||
- "yesterday" → "on [date-1]"
|
||||
- "last week" → "around [specific week]"
|
||||
- "last summer" → "in summer [year] (June-August [year])"
|
||||
- "next month" → "in [month name] [year]"
|
||||
|
||||
## Q&A HANDLING - CRITICAL!
|
||||
### 1.4 occurred_start/end FIELDS ⚠️ CRITICAL
|
||||
|
||||
### WHEN TO COMBINE (simple informational questions):
|
||||
**WHAT THEY REPRESENT:**
|
||||
- occurred_start/end = WHEN THE EVENT ACTUALLY HAPPENED (NOT when it was mentioned!)
|
||||
- These answer: "When did this event occur in reality?"
|
||||
|
||||
**❌ BAD (2 separate facts):**
|
||||
- "James asks what projects John is working on"
|
||||
- "John is working on a website for a local small business"
|
||||
**WHEN TO SET THEM:**
|
||||
✅ SET for datable events (fact_kind="event"):
|
||||
- "went to Tokyo last spring" → occurred_start = March 1, 2024 (spring started)
|
||||
- "accident yesterday" → occurred_start = context date - 1 day
|
||||
- "party next Saturday" → occurred_start = next Saturday's date
|
||||
|
||||
**✅ GOOD (1 combined fact):**
|
||||
- "John is working on a website for a local small business; it's his first professional project outside of class"
|
||||
❌ LEAVE NULL for general info (fact_kind="conversation"):
|
||||
- "loves coffee" → no occurred dates (timeless preference)
|
||||
- "works as engineer" → no occurred dates (ongoing state)
|
||||
- "is expanding business" → no occurred dates (ongoing activity)
|
||||
|
||||
### WHEN TO SPLIT (user requests/instructions to assistant):
|
||||
**KEY DISTINCTION:**
|
||||
- occurred_start/end: When the event happened/will happen
|
||||
- mentioned_at: When this was said/written (set automatically to context date)
|
||||
- These are DIFFERENT! Example: On June 10, saying "went to Tokyo in March" → occurred_start=March, mentioned_at=June 10
|
||||
|
||||
**CRITICAL**: When user asks assistant to DO something, extract BOTH facts separately!
|
||||
**FORMAT:** ISO timestamps "2024-06-15T00:00:00Z"
|
||||
|
||||
**✅ GOOD (2 separate BANK facts):**
|
||||
1. "User requested a children's book about dinosaurs with image placeholders in '::title:: == ::description::' format"
|
||||
2. "I wrote a children's book titled 'The Amazing Adventures of Dinosaurs' with chapters about T-Rex, Pterodactyl, Plesiosaur, and Triceratops, including image descriptions"
|
||||
### 1.5 EXAMPLES - STUDY THESE CAREFULLY
|
||||
|
||||
**❌ BAD (missing user request):**
|
||||
- Only extracting: "I wrote a children's book about dinosaurs..."
|
||||
**Example 1: "yesterday" temporal detection**
|
||||
Input (Context: March 15, 2024): "Hey Taylor! The volunteers were amazing yesterday. But something unexpected happened - a vehicle accident near the center. Everyone was okay though."
|
||||
|
||||
**Rule**: If user says "write...", "create...", "help me...", "explain...", etc. → Extract user's request AND assistant's response as SEPARATE bank facts!
|
||||
Output (3 facts):
|
||||
1. factual_core: "On March 15, 2024, Alex told Taylor that the volunteers were amazing"
|
||||
occurred_start: "2024-03-15T00:00:00Z", entities: ["Alex", "Taylor"]
|
||||
|
||||
## WHAT TO SKIP (only these!)
|
||||
2. factual_core: "On March 15, 2024, Alex mentioned that something unexpected happened the previous day - a vehicle accident"
|
||||
occurred_start: "2024-03-15T00:00:00Z", entities: ["Alex"]
|
||||
|
||||
- **Pure filler with no content** - "Always happy to help", "Sounds good", "Thanks!"
|
||||
- **Greetings** - "Hey!", "What's up?"
|
||||
- **Standalone simple questions that are answered** - merge informational Q&A, but DON'T skip user requests!
|
||||
3. factual_core: "On March 14, 2024, a vehicle accident occurred near the center, but everyone was okay"
|
||||
occurred_start: "2024-03-14T00:00:00Z" ← THE ACTUAL EVENT DATE (yesterday from March 15)
|
||||
|
||||
## WHAT TO ALWAYS EXTRACT
|
||||
**Example 2: "last spring" temporal detection**
|
||||
Input (Context: June 10, 2024): "Casey went to Tokyo last spring. They had an incredible time visiting temples and trying authentic ramen."
|
||||
|
||||
- **USER REQUESTS** (CRITICAL!): "User requested a children's book about dinosaurs", "User asked for help with debugging"
|
||||
- **ASSISTANT ACTIONS**: "I wrote a story", "I recommended meditation", "I explained the concept"
|
||||
- Specific encouragement WITH content: "James says hiccups are normal, use them to learn and grow, push through"
|
||||
- Reactions that reveal preferences: "John says the art is awesome, takes him back to reading fantasy books"
|
||||
- Recommendations: "John recommends 'The Name of the Wind' - great novel with awesome writing"
|
||||
- Plans/intentions: "James will check out 'The Name of the Wind'"
|
||||
- All activities, projects, purchases, events with details
|
||||
Output (2 facts):
|
||||
1. factual_core: "On June 10, 2024, it was mentioned that Casey went to Tokyo the previous spring"
|
||||
occurred_start: "2024-06-10T00:00:00Z", entities: ["Casey", "Tokyo"]
|
||||
|
||||
## ESSENTIAL DETAILS TO PRESERVE - NEVER LOSE THESE
|
||||
2. factual_core: "Casey went to Tokyo in spring 2024 (March-May 2024) and visited temples and tried authentic ramen"
|
||||
occurred_start: "2024-03-01T00:00:00Z", occurred_end: "2024-05-31T23:59:59Z" ← THE ACTUAL EVENT DATES
|
||||
emotional_significance: "Casey had an incredible time in Tokyo"
|
||||
entities: ["Casey", "Tokyo"]
|
||||
|
||||
When extracting facts, you MUST preserve:
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
SECTION 2: EXTRACTION RULES
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
1. **ALL PARTICIPANTS** - Who said/did what
|
||||
2. **INDIVIDUAL PREFERENCES** - Each person's specific likes/favorites! "Jon's favorite is contemporary because it's expressive" - DO NOT LOSE THIS!
|
||||
3. **FULL REASONING** - Why decisions were made, motivations, explanations
|
||||
4. **TEMPORAL CONTEXT - CRITICAL** - ALWAYS convert relative time references to SPECIFIC ABSOLUTE dates in the fact text!
|
||||
- "last week" (doc date Aug 23) → "around August 16, 2023" (NOT just "in August 2023"!)
|
||||
- "last month" (doc date Aug 2023) → "in July 2023"
|
||||
- "yesterday" (doc date Aug 19) → "on August 18, 2023"
|
||||
- "next week" (doc date Aug 19) → "around August 26, 2023"
|
||||
- "three days ago" (doc date Aug 19) → "on August 16, 2023"
|
||||
- "last year" → "in 2022"
|
||||
- BE SPECIFIC! "last week" is NOT "in August" - calculate the actual week!
|
||||
5. **VISUAL/MEDIA ELEMENTS** - Photos, images, videos shared
|
||||
6. **MODIFIERS** - "new", "first", "old", "favorite" (critical context)
|
||||
7. **POSSESSIVE RELATIONSHIPS** - "their kids" → "Person's kids"
|
||||
8. **BIOGRAPHICAL DETAILS** - Origins, locations, jobs, family background
|
||||
9. **SOCIAL DYNAMICS** - Nicknames, how people address each other, relationships
|
||||
### 2.1 WHAT TO EXTRACT
|
||||
✅ User requests to assistant + assistant actions (extract separately)
|
||||
✅ Preferences, recommendations, plans, activities, encouragement (with actual content)
|
||||
✅ Possessions, achievements, metrics, skills, background facts
|
||||
|
||||
## STRUCTURED FACT DIMENSIONS - CRITICAL ⚠️
|
||||
### 2.2 WHAT TO SKIP
|
||||
❌ Greetings, filler ("thanks", "cool"), structural statements
|
||||
|
||||
Each fact MUST be extracted into structured dimensions. This ensures no important context is lost.
|
||||
### 2.3 Q&A HANDLING
|
||||
- Combine simple informational Q&A into one fact
|
||||
- Split user requests to assistant into two facts (request + response)
|
||||
|
||||
**CRITICAL FORMATTING RULE**: Each dimension MUST be a complete, grammatically correct sentence that includes the subject and can stand alone. These dimensions will be combined with " - " separators, so they must read naturally together.
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
SECTION 3: STRUCTURED DIMENSIONS
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
### Required field:
|
||||
- **factual_core**: ACTUAL FACTS - capture WHAT was said, not just THAT something was said!
|
||||
- ❌ BAD: "Jon received encouragement from Gina" (loses what Gina actually said)
|
||||
- ✅ GOOD: "Gina said Jon is the perfect mentor with positivity and determination; his studio will be a hit"
|
||||
- ❌ BAD: "Jon supports Gina" (generic)
|
||||
- ✅ GOOD: "Gina found the perfect spot for her store; Jon says her hard work is paying off"
|
||||
- Preserve: compliments, assessments, descriptions, predictions, key phrases
|
||||
### 3.1 REQUIRED FIELD
|
||||
- **factual_core**: Capture WHAT was said, not just THAT something was said. Complete sentence.
|
||||
|
||||
### Optional fields (include when present in text):
|
||||
- **emotional_significance**: Emotions, feelings, personal meaning, AND qualitative descriptors - COMPLETE SENTENCE with subject
|
||||
- ❌ BAD: "felt thrilled" (fragment, missing subject)
|
||||
- ✅ GOOD: "Sarah felt thrilled about the opportunity"
|
||||
- ❌ BAD: "was her favorite memory" (vague subject)
|
||||
- ✅ GOOD: "This was her favorite memory from childhood"
|
||||
- More examples: "The experience was magical for everyone involved", "John found the loss devastating", "She considers this her proudest moment"
|
||||
- Captures: emotions, intensity, personal significance, AND experiential descriptors ("magical", "wonderful", "amazing", "thrilling", "beautiful")
|
||||
### 3.2 OPTIONAL FIELDS (use when present in text)
|
||||
- **emotional_significance**: Emotions, feelings, qualitative descriptors. Complete sentence with subject.
|
||||
- **reasoning_motivation**: Why it happened, intentions, goals. Complete sentence with subject.
|
||||
- **preferences_opinions**: Likes, dislikes, beliefs, values. Complete sentence with subject. Use for: "ideal", "favorite", "dream", "perfect"
|
||||
- **sensory_details**: Visual, auditory, physical descriptions. Complete sentence. USE EXACT WORDS from text!
|
||||
- **observations**: Background facts, possessions, achievements, metrics, skills. Complete sentence with subject.
|
||||
|
||||
- **reasoning_motivation**: WHY it happened, intentions, goals, causes - COMPLETE SENTENCE with subject
|
||||
- ❌ BAD: "because she wanted to celebrate" (fragment, no subject)
|
||||
- ✅ GOOD: "She did this because she wanted to celebrate with friends"
|
||||
- More examples: "He wrote the book to cope with grief", "She was motivated by curiosity about the topic", "They moved there to be closer to family"
|
||||
- Captures: reasons, intentions, goals, causal explanations
|
||||
### 3.3 FORMATTING RULE
|
||||
Each dimension MUST be a complete, grammatically correct sentence with subject that can stand alone.
|
||||
|
||||
- **preferences_opinions**: Likes, dislikes, beliefs, values, ideals - COMPLETE SENTENCE with subject
|
||||
- ❌ BAD: "loves coffee" (fragment)
|
||||
- ✅ GOOD: "Sarah loves coffee and drinks it every morning"
|
||||
- ❌ BAD: "prefers remote work" (fragment)
|
||||
- ✅ GOOD: "He prefers working remotely over office work"
|
||||
- More examples: "Jon's ideal dance studio would be located by the water", "Jon's favorite dance style is contemporary because it's expressive", "She thinks AI is transformative technology"
|
||||
- Captures: preferences, opinions, beliefs, judgments, ideals, dreams
|
||||
- PREFERENCE INDICATORS: "ideal", "favorite", "dream", "perfect", "love", "hate", "prefer" → MUST capture in this dimension!
|
||||
- CRITICAL: Never lose individual preferences! Always include who has the preference!
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
SECTION 4: FACT CLASSIFICATION
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
- **sensory_details**: Visual, auditory, physical descriptions AND all descriptive adjectives - COMPLETE SENTENCE with subject - USE EXACT WORDS!
|
||||
- ❌ BAD: "bright orange hair" (fragment)
|
||||
- ✅ GOOD: "She has bright orange hair"
|
||||
- ❌ BAD: "so graceful" (fragment)
|
||||
- ✅ GOOD: "The dancer moved so gracefully across the stage"
|
||||
- More examples: "The music was very loud", "The water was freezing cold", "The beach was awesome", "The movie had epic visuals"
|
||||
- Captures: colors, sounds, textures, temperatures, appearances, AND adjectives describing people/things/performances
|
||||
- CRITICAL: Use the EXACT adjectives from the text! If they said "awesome" don't write "amazing". If they said "epic" don't write "perfect"!
|
||||
### 4.1 fact_kind (temporal nature)
|
||||
- **conversation**: General info, ongoing activities (no occurred dates)
|
||||
- **event**: Specific datable occurrence (MUST set occurred_start/end)
|
||||
- **other**: Catch-all
|
||||
|
||||
- **observations**: Things that can be inferred/deduced from the conversation - COMPLETE SENTENCE with subject
|
||||
- ❌ BAD: "traveled to Miami" (fragment)
|
||||
- ✅ GOOD: "Calvin traveled to Miami for the photo shoot"
|
||||
- ❌ BAD: "won dance trophies" (fragment)
|
||||
- ✅ GOOD: "Gina won dance trophies in past competitions"
|
||||
- More examples: "She knows programming from previous projects", "They own a house in the suburbs", "He has experience with public speaking"
|
||||
- TRAVEL: "doing the shoot in Miami" → "Calvin traveled to Miami for the shoot"
|
||||
- POSSESSION: "my trophy" → "She won the trophy"
|
||||
- CAPABILITIES: "she coded it" → "She knows programming"
|
||||
### 4.2 fact_type (subject matter)
|
||||
- **world**: Everything NOT involving assistant (user background, other people, events)
|
||||
- **assistant**: Interactions BY or TO assistant (requests, recommendations, actions in THIS conversation)
|
||||
|
||||
### Example extraction:
|
||||
Rule: If it would exist without this conversation → world. If only exists because of this conversation → assistant.
|
||||
|
||||
**Input**: "I used to compete in dance competitions - my fav memory was when my team won first place at regionals at age fifteen. It was an awesome feeling of accomplishment!"
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
SECTION 5: ENTITIES & CAUSALITY
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
**Output**:
|
||||
```
|
||||
factual_core: "Gina's team won first place at a regional dance competition when she was 15"
|
||||
emotional_significance: "This was Gina's favorite memory; she felt an awesome sense of accomplishment"
|
||||
reasoning_motivation: null
|
||||
preferences_opinions: null
|
||||
sensory_details: null
|
||||
```
|
||||
### 5.1 ENTITIES
|
||||
Extract: People names, organizations, specific places, products
|
||||
Skip: Generic relations (mom, friend), pronouns, common nouns
|
||||
|
||||
**Combined result**: "Gina's team won first place at a regional dance competition when she was 15 - This was Gina's favorite memory; she felt an awesome sense of accomplishment"
|
||||
### 5.2 CAUSAL RELATIONS
|
||||
Link facts when explicit causation: causes, caused_by, enables, prevents"""
|
||||
|
||||
### CRITICAL: Never strip away dimensions!
|
||||
- ❌ BAD: Only extracting factual_core and ignoring emotional context
|
||||
- ✅ GOOD: Capturing ALL dimensions present in the text
|
||||
- ❌ BAD: Using fragments like "felt happy" or "loves pizza"
|
||||
- ✅ GOOD: Using complete sentences like "She felt happy about the news" or "John loves pizza and orders it weekly"
|
||||
|
||||
## TEMPORAL CLASSIFICATION (fact_kind field) - About WHEN/TIMING
|
||||
|
||||
⚠️ **WARNING**: Do NOT confuse fact_kind with fact_type (see below)! These are DIFFERENT fields!
|
||||
|
||||
### fact_kind determines if occurred dates are set:
|
||||
|
||||
**`conversation`** - General info, activities, preferences, ongoing things
|
||||
- NO occurred_start/end (leave null)
|
||||
- Examples: "Jon is expanding his studio", "Jon loves dance", "Gina's ideal studio is by water"
|
||||
|
||||
**`event`** - Specific datable occurrence (competition, wedding, meeting, trip, loss, start/end of something)
|
||||
- MUST set occurred_start/end
|
||||
- Ask: "Is this a SPECIFIC EVENT with a DATE?"
|
||||
- Examples: "Dance competition on May 15", "Lost job in January 2023", "Wedding next Saturday"
|
||||
|
||||
**`other`** - Anything else that doesn't fit above
|
||||
- NO occurred_start/end (leave null)
|
||||
- Catch-all to not lose information
|
||||
|
||||
### Rules:
|
||||
1. **ALWAYS include dates in fact text** - "in January 2023", "on May 15, 2024"
|
||||
2. **Only 'event' gets occurred dates** - conversation and other = null
|
||||
3. **SPLIT events from conversation facts** - "Jon is expanding his studio (conversation) and hosting a competition next month (event)" → 2 separate facts!
|
||||
|
||||
## CAUSAL RELATIONSHIPS
|
||||
|
||||
When splitting related facts, link them with causal_relations:
|
||||
- **causes**: This fact causes the target
|
||||
- **caused_by**: This fact was caused by target
|
||||
- **enables/prevents**: This fact enables/prevents the target
|
||||
|
||||
Only link when there's explicit or clear implicit causation ("because", "so", "therefore").
|
||||
|
||||
## FACT TYPE CLASSIFICATION - The Simple Rule
|
||||
|
||||
⚠️ **WARNING**: Do NOT confuse fact_type with fact_kind (see above)! These are DIFFERENT fields!
|
||||
- fact_kind = temporal nature (conversation/event/other)
|
||||
- fact_type = who/what this is about (world/assistant)
|
||||
|
||||
### The Rule: Everything NOT involving the assistant = 'world'
|
||||
|
||||
- **'world'**: Facts about people, places, events, things that exist independently of assistant interactions
|
||||
- **User's background/experience**: "User worked as marketing specialist at startup", "User has 5 years of Python experience"
|
||||
- **User's skills/knowledge**: "User has used Trello", "User is familiar with Kanban methodology", "User knows React"
|
||||
- **User's preferences/interests**: "User prefers async communication", "User is interested in exploring project management tools"
|
||||
- **Other people's lives**: "Sarah got promoted", "John traveled to Paris", "Mom retired last year"
|
||||
- **Events and facts**: "The meeting was cancelled", "The project launched in 2023"
|
||||
- **RULE**: If it would still be true even if this conversation never happened → **world**
|
||||
|
||||
- **'assistant'**: Interactions BY or TO the assistant (what happened in THIS conversation)
|
||||
- **User's questions/requests TO assistant**: "User asked about ClickUp features", "User requested comparison between tools", "User wanted to know strengths and weaknesses"
|
||||
- **Assistant's actions/responses**: "I recommended trying meditation", "I explained the difference between Trello and ClickUp", "I suggested exploring alternatives"
|
||||
- **Conversational events**: "User thanked me for the suggestion", "I clarified the technical details"
|
||||
- Use "user" or their name for user's questions/requests
|
||||
- Use FIRST PERSON ("I") for assistant's actions
|
||||
- **RULE**: If this only exists because of this conversation with the assistant → **assistant**
|
||||
|
||||
**CRITICAL EXAMPLES**:
|
||||
- "User worked at startup" → **world** (would be true even without this conversation)
|
||||
- "User asked me about ClickUp" → **assistant** (only exists because of this conversation)
|
||||
- "User has experience with Trello" → **world** (independent fact about user)
|
||||
- "User wanted to explore options" → Could be either:
|
||||
- **world** if it's a general preference: "User is interested in exploring project management alternatives"
|
||||
- **assistant** if it's what they expressed in this conversation: "User asked me to help explore other options"
|
||||
|
||||
**Real Example**:
|
||||
User says: "I've used Trello in my previous role as a marketing specialist at a small startup and I'm familiar with its features. But I'm interested in exploring other options as well. Could you tell me more about ClickUp?"
|
||||
|
||||
Extract these facts:
|
||||
1. **world**: "User worked as marketing specialist at small startup"
|
||||
2. **world**: "User has used Trello in previous role"
|
||||
3. **world**: "User is familiar with Trello features"
|
||||
4. **world**: "User is interested in exploring project management alternatives"
|
||||
5. **assistant**: "User asked me about ClickUp and how it differs from Trello"
|
||||
|
||||
**Speaker attribution**: If context says "Your name: Marcus", extract 'assistant' facts from both "Marcus:" and "Assistant:" lines.
|
||||
|
||||
## WHAT TO SKIP
|
||||
- Greetings, filler words, pure reactions ("wow", "cool")
|
||||
- Structural statements ("let's get started", "see you next time")
|
||||
- Calls to action ("subscribe", "follow")
|
||||
|
||||
## EXAMPLE: SPLITTING CONVERSATION VS EVENT FACTS
|
||||
|
||||
**Input (conversation date: April 3, 2023):**
|
||||
"I'm expanding my dance studio's social media presence and offering workshops to local schools. I'm also hosting a dance competition next month to showcase local talent. The dancers are so excited!"
|
||||
|
||||
**Output (2 facts - conversation + event):**
|
||||
|
||||
**Fact 1 (kind=conversation - ongoing activities, no occurred dates):**
|
||||
```
|
||||
fact_kind: "conversation"
|
||||
factual_core: "Jon is expanding his dance studio's social media presence in April 2023; offering workshops and classes to local schools and centers; seeing progress and dancers are excited"
|
||||
emotional_significance: "excited and proud of progress"
|
||||
preferences_opinions: "Jon loves giving dancers a place to express themselves"
|
||||
observations: "Jon owns/runs a dance studio"
|
||||
occurred_start: null ← conversation kind = no occurred dates
|
||||
occurred_end: null
|
||||
```
|
||||
|
||||
**Fact 2 (kind=event - specific datable occurrence):**
|
||||
```
|
||||
fact_kind: "event"
|
||||
factual_core: "Jon will host a dance competition in May 2023 to showcase local talent and bring attention to his studio"
|
||||
emotional_significance: "excited about the event"
|
||||
occurred_start: "2023-05-01T00:00:00Z" ← event kind = HAS occurred dates
|
||||
occurred_end: "2023-05-31T23:59:59Z"
|
||||
```
|
||||
|
||||
**❌ BAD:** Combining both into one fact with occurred=May (makes ongoing activities look like they happened in May!)
|
||||
|
||||
## TEXT TO EXTRACT FROM:
|
||||
{chunk}
|
||||
|
||||
## CRITICAL REMINDERS:
|
||||
1. **NEVER MISS USER REQUESTS** - If user asks assistant to do something ("write...", "create...", "help me..."), extract BOTH the request AND the response as separate BANK facts!
|
||||
2. **BANK FACT PERSPECTIVE** - Use "I" for assistant actions ("I recommended", "I wrote"), use "user" or their name for user actions ("User requested", "Marcus said")
|
||||
3. **COMBINE SIMPLE Q&A** - Merge simple informational questions with answers. But don't merge user requests - extract them separately!
|
||||
4. **CAPTURE ALL MEANINGFUL CONTENT** - Activities, encouragement (with specific words!), recommendations, reactions, preferences
|
||||
5. **CONVERT RELATIVE DATES TO SPECIFIC DATES** - "last week" → "around August 16" (NOT "in August"!), "yesterday" → "on August 18". Be precise!
|
||||
6. **CAPTURE WHAT WAS SAID** - "Gina said Jon is perfect mentor with determination" NOT "Jon received encouragement". Preserve the actual content!
|
||||
7. **FACT_KIND DETERMINES OCCURRED DATES** - Only 'event' gets occurred_start/end. 'conversation' and 'other' = null
|
||||
8. **CAPTURE PREFERENCES** - "ideal", "favorite", "love" → preferences_opinions
|
||||
9. **CAPTURE EXACT ADJECTIVES** - Use the EXACT words! "awesome" not "amazing", "epic" not "perfect" → sensory_details
|
||||
10. **CAPTURE OBSERVATIONS** - "shooting in Miami" → observations: "traveled to Miami". Infer travel, achievements, capabilities!"""
|
||||
|
||||
import logging
|
||||
from openai import BadRequestError
|
||||
|
|
@ -559,19 +407,25 @@ occurred_end: "2023-05-31T23:59:59Z"
|
|||
max_retries = 2
|
||||
last_error = None
|
||||
|
||||
# inject all the chunk metadata for better reasoning
|
||||
chunk_data = json.dumps({
|
||||
"chunk_index": chunk_index,
|
||||
"total_chunks": total_chunks,
|
||||
"event_date": event_date.isoformat(),
|
||||
"context": context,
|
||||
"chunk_content": chunk
|
||||
})
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
# Get raw JSON response without strict Pydantic validation
|
||||
# We'll handle the data leniently to be resilient to LLM weirdness
|
||||
extraction_response_json = await llm_config.call(
|
||||
messages=[
|
||||
{
|
||||
"role": "system",
|
||||
"content": "Extract ALL meaningful content. NEVER MISS USER REQUESTS - if user asks assistant to do something ('write...', 'create...', 'help me...'), extract BOTH request AND response as separate BANK facts! COMBINE simple informational Q&A. BANK facts: use 'I' for assistant actions ('I recommended'), use 'user'/name for user actions ('User requested', 'Marcus said'). CONVERT RELATIVE DATES TO SPECIFIC DATES ('last week' → 'around Aug 16' NOT 'in August'!). factual_core = WHAT was said, not THAT something was said! fact_kind: 'conversation'/'event'/'other'. Only 'event' gets occurred dates. Optional fields: include 'entities', 'causal_relations', 'occurred_start', 'occurred_end', 'emotional_significance', 'reasoning_motivation', 'preferences_opinions', 'sensory_details', 'observations' only if they have meaningful values (can omit if not applicable)."
|
||||
"content": prompt
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": prompt
|
||||
"content": chunk_data
|
||||
}
|
||||
],
|
||||
response_format=FactExtractionResponse,
|
||||
|
|
@ -596,7 +450,7 @@ occurred_end: "2023-05-31T23:59:59Z"
|
|||
if not raw_facts:
|
||||
logger.warning(
|
||||
f"LLM response missing 'facts' field or returned empty list. "
|
||||
f"Keys: {list(extraction_response_json.keys())}"
|
||||
f"Response: {extraction_response_json}"
|
||||
)
|
||||
|
||||
for i, llm_fact in enumerate(raw_facts):
|
||||
|
|
@ -653,6 +507,9 @@ occurred_end: "2023-05-31T23:59:59Z"
|
|||
'sensory_details', 'observations']:
|
||||
value = get_value(field)
|
||||
if value:
|
||||
# Handle case where LLM returns list instead of string
|
||||
if isinstance(value, list):
|
||||
value = '; '.join(str(v) for v in value)
|
||||
fact_data[field] = value
|
||||
dimension_parts.append(value)
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ async def insert_facts_batch(
|
|||
# Prepare data for batch insert
|
||||
fact_texts = []
|
||||
embeddings = []
|
||||
event_dates = []
|
||||
occurred_starts = []
|
||||
occurred_ends = []
|
||||
mentioned_ats = []
|
||||
|
|
@ -52,6 +53,9 @@ async def insert_facts_batch(
|
|||
fact_texts.append(fact.fact_text)
|
||||
# Convert embedding to string for asyncpg vector type
|
||||
embeddings.append(str(fact.embedding))
|
||||
# event_date: Use occurred_start if available, otherwise use mentioned_at
|
||||
# This maintains backward compatibility while handling None occurred_start
|
||||
event_dates.append(fact.occurred_start if fact.occurred_start is not None else fact.mentioned_at)
|
||||
occurred_starts.append(fact.occurred_start)
|
||||
occurred_ends.append(fact.occurred_end)
|
||||
mentioned_ats.append(fact.mentioned_at)
|
||||
|
|
@ -65,7 +69,6 @@ async def insert_facts_batch(
|
|||
document_ids.append(document_id)
|
||||
|
||||
# Batch insert all facts
|
||||
# Note: event_date is set to occurred_start for backward compatibility
|
||||
results = await conn.fetch(
|
||||
"""
|
||||
INSERT INTO memory_units (bank_id, text, embedding, event_date, occurred_start, occurred_end, mentioned_at,
|
||||
|
|
@ -79,7 +82,7 @@ async def insert_facts_batch(
|
|||
bank_id,
|
||||
fact_texts,
|
||||
embeddings,
|
||||
occurred_starts, # event_date (for backward compatibility)
|
||||
event_dates, # event_date: occurred_start if available, else mentioned_at
|
||||
occurred_starts,
|
||||
occurred_ends,
|
||||
mentioned_ats,
|
||||
|
|
|
|||
|
|
@ -102,8 +102,8 @@ class ProcessedFact:
|
|||
embedding: List[float]
|
||||
|
||||
# Temporal data
|
||||
occurred_start: datetime
|
||||
occurred_end: datetime
|
||||
occurred_start: Optional[datetime]
|
||||
occurred_end: Optional[datetime]
|
||||
mentioned_at: datetime
|
||||
|
||||
# Context and metadata
|
||||
|
|
@ -146,9 +146,9 @@ class ProcessedFact:
|
|||
"""
|
||||
from datetime import datetime, timezone
|
||||
|
||||
# Use occurred dates if available, otherwise use mentioned_at
|
||||
occurred_start = extracted_fact.occurred_start or extracted_fact.mentioned_at
|
||||
occurred_end = extracted_fact.occurred_end or extracted_fact.mentioned_at
|
||||
# Use occurred dates only if explicitly provided by LLM
|
||||
occurred_start = extracted_fact.occurred_start
|
||||
occurred_end = extracted_fact.occurred_end
|
||||
mentioned_at = extracted_fact.mentioned_at or datetime.now(timezone.utc)
|
||||
|
||||
# Convert entity strings to EntityRef objects
|
||||
|
|
|
|||
|
|
@ -141,10 +141,16 @@ class EmbeddedPostgres:
|
|||
Downloads and installs the binary if not already present.
|
||||
"""
|
||||
if self.is_installed():
|
||||
logger.info(f"pg0 already installed at {self.binary_path}")
|
||||
return
|
||||
|
||||
logger.info("Installing pg0 CLI...")
|
||||
|
||||
# Log platform information
|
||||
binary_name = get_platform_binary_name()
|
||||
logger.info(f"Detected platform: system={platform.system()}, machine={platform.machine()}")
|
||||
logger.info(f"Will download binary: {binary_name}")
|
||||
|
||||
# Create install directory
|
||||
self.install_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
|
|
|||
69
hindsight-api/test_mentioned_at.py
Normal file
69
hindsight-api/test_mentioned_at.py
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
"""Test to verify mentioned_at uses event_date, not now()"""
|
||||
import asyncio
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from hindsight_api.engine.memory_engine import MemoryEngine
|
||||
|
||||
async def test_mentioned_at_uses_event_date():
|
||||
"""Verify that mentioned_at is set to event_date, not now()"""
|
||||
|
||||
# Use a date that's clearly not "now"
|
||||
past_date = datetime(2020, 1, 1, 12, 0, 0, tzinfo=timezone.utc)
|
||||
|
||||
memory = MemoryEngine()
|
||||
await memory.initialize()
|
||||
|
||||
try:
|
||||
bank_id = "test_mentioned_at_debug"
|
||||
|
||||
# Store with explicit past event_date
|
||||
unit_ids = await memory.retain_async(
|
||||
bank_id=bank_id,
|
||||
content="Alex went to the store.",
|
||||
context="test",
|
||||
event_date=past_date
|
||||
)
|
||||
|
||||
print(f"\n✅ Stored {len(unit_ids)} units")
|
||||
|
||||
# Recall and check mentioned_at
|
||||
result = await memory.recall_async(
|
||||
bank_id=bank_id,
|
||||
query="store",
|
||||
max_tokens=500
|
||||
)
|
||||
|
||||
print(f"✅ Found {len(result.results)} facts")
|
||||
|
||||
for i, fact in enumerate(result.results, 1):
|
||||
print(f"\nFact {i}:")
|
||||
print(f" Text: {fact.text[:80]}...")
|
||||
print(f" mentioned_at: {fact.mentioned_at}")
|
||||
print(f" occurred_start: {fact.occurred_start}")
|
||||
|
||||
# Parse mentioned_at
|
||||
if isinstance(fact.mentioned_at, str):
|
||||
mentioned_dt = datetime.fromisoformat(fact.mentioned_at.replace('Z', '+00:00'))
|
||||
else:
|
||||
mentioned_dt = fact.mentioned_at
|
||||
|
||||
# Check if mentioned_at matches our event_date
|
||||
time_diff = abs((mentioned_dt - past_date).total_seconds())
|
||||
|
||||
if time_diff < 60:
|
||||
print(f" ✅ mentioned_at correctly set to event_date")
|
||||
else:
|
||||
print(f" ❌ mentioned_at is {mentioned_dt}, expected {past_date}")
|
||||
print(f" Time difference: {time_diff} seconds")
|
||||
|
||||
# Check if it's close to now()
|
||||
now_diff = abs((mentioned_dt - datetime.now(timezone.utc)).total_seconds())
|
||||
if now_diff < 60:
|
||||
print(f" ⚠️ mentioned_at is using now() instead of event_date!")
|
||||
|
||||
await memory.delete_bank(bank_id)
|
||||
|
||||
finally:
|
||||
await memory.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(test_mentioned_at_uses_event_date())
|
||||
|
|
@ -2,9 +2,12 @@
|
|||
Test retain function and chunk storage.
|
||||
"""
|
||||
import pytest
|
||||
import logging
|
||||
from datetime import datetime, timezone
|
||||
from hindsight_api.engine.memory_engine import Budget
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_retain_with_chunks(memory):
|
||||
|
|
@ -415,6 +418,95 @@ async def test_mentioned_at_vs_occurred(memory):
|
|||
await memory.delete_bank(bank_id)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_occurred_dates_not_defaulted(memory):
|
||||
"""
|
||||
Test that occurred_start and occurred_end are NOT defaulted to mentioned_at.
|
||||
|
||||
This is a regression test for a bug where occurred dates were incorrectly
|
||||
defaulting to mentioned_at when the LLM didn't provide them.
|
||||
|
||||
Scenario: Store a fact where occurred dates are not applicable (current observation)
|
||||
- mentioned_at should be set (to event_date or now())
|
||||
- occurred_start and occurred_end should be None (not defaulted to mentioned_at)
|
||||
"""
|
||||
bank_id = f"test_occurred_not_defaulted_{datetime.now(timezone.utc).timestamp()}"
|
||||
|
||||
try:
|
||||
# Store a current observation where occurred dates don't make sense
|
||||
# Use present tense to avoid LLM extracting past dates
|
||||
event_date = datetime(2024, 2, 10, 15, 30, tzinfo=timezone.utc)
|
||||
|
||||
unit_ids = await memory.retain_async(
|
||||
bank_id=bank_id,
|
||||
content="Alice likes coffee. The weather is sunny today.",
|
||||
context="current observations",
|
||||
event_date=event_date
|
||||
)
|
||||
|
||||
assert len(unit_ids) > 0, "Should create memory unit"
|
||||
|
||||
# Recall and check that occurred dates are None
|
||||
result = await memory.recall_async(
|
||||
bank_id=bank_id,
|
||||
query="What does Alice like?",
|
||||
budget=Budget.LOW,
|
||||
max_tokens=500,
|
||||
fact_type=["world", "opinion"]
|
||||
)
|
||||
|
||||
assert len(result.results) > 0, "Should recall the fact"
|
||||
fact = result.results[0]
|
||||
|
||||
# mentioned_at should be set
|
||||
assert fact.mentioned_at is not None, "mentioned_at should be set"
|
||||
|
||||
# Parse mentioned_at
|
||||
if isinstance(fact.mentioned_at, str):
|
||||
mentioned_dt = datetime.fromisoformat(fact.mentioned_at.replace('Z', '+00:00'))
|
||||
else:
|
||||
mentioned_dt = fact.mentioned_at
|
||||
|
||||
# Verify it matches event_date
|
||||
time_diff = abs((event_date - mentioned_dt).total_seconds())
|
||||
assert time_diff < 60, f"mentioned_at should match event_date, but diff is {time_diff}s"
|
||||
|
||||
# CRITICAL: occurred_start and occurred_end should be None
|
||||
# They should NOT default to mentioned_at
|
||||
if fact.occurred_start is not None:
|
||||
# If occurred_start is set, it means the LLM extracted it
|
||||
# In this case, log it but don't fail (LLM behavior can vary)
|
||||
print(f"⚠ LLM extracted occurred_start: {fact.occurred_start}")
|
||||
print(f" This test expects None for present-tense observations")
|
||||
else:
|
||||
print(f"✓ occurred_start is correctly None (not defaulted to mentioned_at)")
|
||||
|
||||
if fact.occurred_end is not None:
|
||||
print(f"⚠ LLM extracted occurred_end: {fact.occurred_end}")
|
||||
print(f" This test expects None for present-tense observations")
|
||||
else:
|
||||
print(f"✓ occurred_end is correctly None (not defaulted to mentioned_at)")
|
||||
|
||||
# At least verify they're not equal to mentioned_at if they are set
|
||||
if fact.occurred_start is not None:
|
||||
if isinstance(fact.occurred_start, str):
|
||||
occurred_start_dt = datetime.fromisoformat(fact.occurred_start.replace('Z', '+00:00'))
|
||||
else:
|
||||
occurred_start_dt = fact.occurred_start
|
||||
|
||||
# If they're equal, it suggests the old defaulting bug
|
||||
if occurred_start_dt == mentioned_dt:
|
||||
raise AssertionError(
|
||||
f"occurred_start should NOT be defaulted to mentioned_at! "
|
||||
f"occurred_start={occurred_start_dt}, mentioned_at={mentioned_dt}"
|
||||
)
|
||||
|
||||
print(f"✓ Test passed: occurred dates are not incorrectly defaulted to mentioned_at")
|
||||
|
||||
finally:
|
||||
await memory.delete_bank(bank_id)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_mentioned_at_from_context_string(memory):
|
||||
"""
|
||||
|
|
@ -1107,3 +1199,399 @@ async def test_chunks_truncation_behavior(memory):
|
|||
|
||||
finally:
|
||||
await memory.delete_bank(bank_id)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Memory Links Tests
|
||||
# ============================================================
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_temporal_links_creation(memory):
|
||||
"""
|
||||
Test that temporal links are created between facts with nearby event dates.
|
||||
|
||||
Temporal links connect facts that occurred close in time (within 24 hours).
|
||||
"""
|
||||
bank_id = f"test_temporal_links_{datetime.now(timezone.utc).timestamp()}"
|
||||
|
||||
try:
|
||||
# Store facts with nearby timestamps (within 24 hours)
|
||||
base_date = datetime(2024, 1, 15, 10, 0, 0, tzinfo=timezone.utc)
|
||||
|
||||
# Fact 1 at 10:00 AM
|
||||
unit_ids_1 = await memory.retain_async(
|
||||
bank_id=bank_id,
|
||||
content="Alice started working on the authentication module.",
|
||||
context="daily standup",
|
||||
event_date=base_date
|
||||
)
|
||||
|
||||
# Fact 2 at 2:00 PM same day (4 hours later)
|
||||
unit_ids_2 = await memory.retain_async(
|
||||
bank_id=bank_id,
|
||||
content="Bob reviewed the API design document.",
|
||||
context="daily standup",
|
||||
event_date=base_date.replace(hour=14)
|
||||
)
|
||||
|
||||
# Fact 3 at 9:00 AM next day (23 hours later)
|
||||
unit_ids_3 = await memory.retain_async(
|
||||
bank_id=bank_id,
|
||||
content="Charlie deployed the new database schema.",
|
||||
context="daily standup",
|
||||
event_date=base_date.replace(day=16, hour=9)
|
||||
)
|
||||
|
||||
assert len(unit_ids_1) > 0 and len(unit_ids_2) > 0 and len(unit_ids_3) > 0
|
||||
|
||||
logger.info(f"Created {len(unit_ids_1) + len(unit_ids_2) + len(unit_ids_3)} facts")
|
||||
|
||||
# Query the memory_links table to verify temporal links exist
|
||||
async with memory._pool.acquire() as conn:
|
||||
# Get all temporal links for these units
|
||||
all_unit_ids = unit_ids_1 + unit_ids_2 + unit_ids_3
|
||||
|
||||
temporal_links = await conn.fetch(
|
||||
"""
|
||||
SELECT from_unit_id, to_unit_id, link_type, weight
|
||||
FROM memory_links
|
||||
WHERE from_unit_id::text = ANY($1)
|
||||
AND link_type = 'temporal'
|
||||
ORDER BY weight DESC
|
||||
""",
|
||||
all_unit_ids
|
||||
)
|
||||
|
||||
logger.info(f"Found {len(temporal_links)} temporal links")
|
||||
|
||||
# Should have temporal links between the facts
|
||||
assert len(temporal_links) > 0, "Should have created temporal links between facts with nearby dates"
|
||||
|
||||
# Verify link properties
|
||||
for link in temporal_links:
|
||||
from_id = str(link['from_unit_id'])
|
||||
to_id = str(link['to_unit_id'])
|
||||
logger.info(f" Link: {from_id[:8]}... -> {to_id[:8]}... (weight: {link['weight']:.2f})")
|
||||
assert link['link_type'] == 'temporal', "Link type should be 'temporal'"
|
||||
assert 0.0 <= link['weight'] <= 1.0, "Weight should be between 0 and 1"
|
||||
|
||||
logger.info("Temporal links created successfully with proper weights")
|
||||
|
||||
finally:
|
||||
await memory.delete_bank(bank_id)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_semantic_links_creation(memory):
|
||||
"""
|
||||
Test that semantic links are created between facts with similar content.
|
||||
|
||||
Semantic links connect facts that are semantically similar based on embeddings.
|
||||
"""
|
||||
bank_id = f"test_semantic_links_{datetime.now(timezone.utc).timestamp()}"
|
||||
|
||||
try:
|
||||
# Store facts with similar semantic content
|
||||
unit_ids_1 = await memory.retain_async(
|
||||
bank_id=bank_id,
|
||||
content="Alice is an expert in Python programming and has built many web applications.",
|
||||
context="team skills"
|
||||
)
|
||||
|
||||
# Similar content - should create semantic link
|
||||
unit_ids_2 = await memory.retain_async(
|
||||
bank_id=bank_id,
|
||||
content="Bob is proficient in Python development and specializes in building APIs.",
|
||||
context="team skills"
|
||||
)
|
||||
|
||||
# Different content - less likely to create strong semantic link
|
||||
unit_ids_3 = await memory.retain_async(
|
||||
bank_id=bank_id,
|
||||
content="The quarterly sales meeting is scheduled for next Tuesday at 3 PM.",
|
||||
context="calendar events"
|
||||
)
|
||||
|
||||
assert len(unit_ids_1) > 0 and len(unit_ids_2) > 0 and len(unit_ids_3) > 0
|
||||
|
||||
logger.info(f"Created {len(unit_ids_1) + len(unit_ids_2) + len(unit_ids_3)} facts")
|
||||
|
||||
# Query the memory_links table to verify semantic links exist
|
||||
async with memory._pool.acquire() as conn:
|
||||
all_unit_ids = unit_ids_1 + unit_ids_2 + unit_ids_3
|
||||
|
||||
semantic_links = await conn.fetch(
|
||||
"""
|
||||
SELECT from_unit_id, to_unit_id, link_type, weight
|
||||
FROM memory_links
|
||||
WHERE from_unit_id::text = ANY($1)
|
||||
AND link_type = 'semantic'
|
||||
ORDER BY weight DESC
|
||||
""",
|
||||
all_unit_ids
|
||||
)
|
||||
|
||||
logger.info(f"Found {len(semantic_links)} semantic links")
|
||||
|
||||
# Should have semantic links between similar facts
|
||||
assert len(semantic_links) > 0, "Should have created semantic links between similar facts"
|
||||
|
||||
# Verify link properties
|
||||
for link in semantic_links:
|
||||
from_id = str(link['from_unit_id'])
|
||||
to_id = str(link['to_unit_id'])
|
||||
logger.info(f" Link: {from_id[:8]}... -> {to_id[:8]}... (weight: {link['weight']:.3f})")
|
||||
assert link['link_type'] == 'semantic', "Link type should be 'semantic'"
|
||||
assert 0.0 <= link['weight'] <= 1.0, "Weight should be between 0 and 1"
|
||||
# Semantic links typically have weight >= 0.7 (threshold)
|
||||
assert link['weight'] >= 0.7, f"Semantic links should have weight >= 0.7, got {link['weight']}"
|
||||
|
||||
logger.info("Semantic links created successfully between similar content")
|
||||
|
||||
finally:
|
||||
await memory.delete_bank(bank_id)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_entity_links_creation(memory):
|
||||
"""
|
||||
Test that entity links are created between facts that mention the same entities.
|
||||
|
||||
Entity links connect facts that reference the same person, place, or concept.
|
||||
This is core functionality and should work consistently.
|
||||
"""
|
||||
bank_id = f"test_entity_links_{datetime.now(timezone.utc).timestamp()}"
|
||||
|
||||
try:
|
||||
# Store facts that mention the same entities
|
||||
unit_ids_1 = await memory.retain_async(
|
||||
bank_id=bank_id,
|
||||
content="Alice joined Google as a software engineer in 2020.",
|
||||
context="career history"
|
||||
)
|
||||
|
||||
# Mentions same entity (Alice) - should create entity link
|
||||
unit_ids_2 = await memory.retain_async(
|
||||
bank_id=bank_id,
|
||||
content="Alice led the development of the new authentication system.",
|
||||
context="project updates"
|
||||
)
|
||||
|
||||
# Mentions same entity (Google) - should create entity link
|
||||
unit_ids_3 = await memory.retain_async(
|
||||
bank_id=bank_id,
|
||||
content="Google announced new cloud services at their annual conference.",
|
||||
context="tech news"
|
||||
)
|
||||
|
||||
# Different entities - no entity link expected
|
||||
unit_ids_4 = await memory.retain_async(
|
||||
bank_id=bank_id,
|
||||
content="Bob works at Meta on machine learning infrastructure.",
|
||||
context="career history"
|
||||
)
|
||||
|
||||
assert len(unit_ids_1) > 0 and len(unit_ids_2) > 0 and len(unit_ids_3) > 0 and len(unit_ids_4) > 0
|
||||
|
||||
logger.info(f"Created {len(unit_ids_1) + len(unit_ids_2) + len(unit_ids_3) + len(unit_ids_4)} facts")
|
||||
|
||||
# Query the memory_links table to verify entity links exist
|
||||
async with memory._pool.acquire() as conn:
|
||||
all_unit_ids = unit_ids_1 + unit_ids_2 + unit_ids_3 + unit_ids_4
|
||||
|
||||
entity_links = await conn.fetch(
|
||||
"""
|
||||
SELECT from_unit_id, to_unit_id, link_type, weight, entity_id
|
||||
FROM memory_links
|
||||
WHERE from_unit_id::text = ANY($1)
|
||||
AND link_type = 'entity'
|
||||
ORDER BY from_unit_id, to_unit_id
|
||||
""",
|
||||
all_unit_ids
|
||||
)
|
||||
|
||||
logger.info(f"Found {len(entity_links)} entity links")
|
||||
|
||||
# Entity extraction is core functionality and should work
|
||||
assert len(entity_links) > 0, "Should have created entity links between facts with shared entities (Alice, Google)"
|
||||
|
||||
# Verify link properties
|
||||
entities_seen = set()
|
||||
for link in entity_links:
|
||||
entity_id = link['entity_id']
|
||||
entities_seen.add(str(entity_id))
|
||||
from_id = str(link['from_unit_id'])
|
||||
to_id = str(link['to_unit_id'])
|
||||
logger.info(f" Link: {from_id[:8]}... -> {to_id[:8]}... via entity {str(entity_id)[:8]}...")
|
||||
assert link['link_type'] == 'entity', "Link type should be 'entity'"
|
||||
assert link['weight'] == 1.0, "Entity links should have weight 1.0"
|
||||
assert entity_id is not None, "Entity links must reference an entity_id"
|
||||
|
||||
logger.info(f"Entity links created successfully for {len(entities_seen)} unique entities")
|
||||
|
||||
# Verify bidirectional links (entity links should be bidirectional)
|
||||
link_pairs = set()
|
||||
for link in entity_links:
|
||||
from_id = str(link['from_unit_id'])
|
||||
to_id = str(link['to_unit_id'])
|
||||
entity_id = str(link['entity_id'])
|
||||
link_pairs.add((from_id, to_id, entity_id))
|
||||
|
||||
# Check that for each (A -> B) link, there's a (B -> A) link with same entity
|
||||
for from_id, to_id, entity_id in link_pairs:
|
||||
reverse_exists = (to_id, from_id, entity_id) in link_pairs
|
||||
assert reverse_exists, f"Entity links should be bidirectional: missing reverse link for {from_id[:8]} -> {to_id[:8]}"
|
||||
|
||||
logger.info("Entity links are properly bidirectional")
|
||||
|
||||
finally:
|
||||
await memory.delete_bank(bank_id)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_causal_links_creation(memory):
|
||||
"""
|
||||
Test that causal links are created between facts with causal relationships.
|
||||
|
||||
Causal links connect facts where one causes, enables, or prevents another.
|
||||
Note: This depends on LLM extracting causal relationships, which may be non-deterministic.
|
||||
"""
|
||||
bank_id = f"test_causal_links_{datetime.now(timezone.utc).timestamp()}"
|
||||
|
||||
try:
|
||||
# Store content with explicit causal relationships
|
||||
# Using clear cause-and-effect language to maximize LLM detection
|
||||
content = """
|
||||
Alice completed the authentication module on Monday. Because Alice finished the auth module,
|
||||
Bob was able to start integrating it with the API on Tuesday. Bob's API integration enabled
|
||||
Charlie to begin testing the complete user flow on Wednesday. The successful testing caused
|
||||
the team to schedule the production deployment for Friday.
|
||||
"""
|
||||
|
||||
unit_ids = await memory.retain_async(
|
||||
bank_id=bank_id,
|
||||
content=content,
|
||||
context="project timeline"
|
||||
)
|
||||
|
||||
assert len(unit_ids) > 0, "Should have created facts"
|
||||
logger.info(f"Created {len(unit_ids)} facts from causal content")
|
||||
|
||||
# Query the memory_links table to check for causal links
|
||||
async with memory._pool.acquire() as conn:
|
||||
causal_links = await conn.fetch(
|
||||
"""
|
||||
SELECT from_unit_id, to_unit_id, link_type, weight
|
||||
FROM memory_links
|
||||
WHERE from_unit_id::text = ANY($1)
|
||||
AND link_type IN ('causes', 'caused_by', 'enables', 'prevents')
|
||||
ORDER BY link_type, weight DESC
|
||||
""",
|
||||
unit_ids
|
||||
)
|
||||
|
||||
logger.info(f"Found {len(causal_links)} causal links")
|
||||
|
||||
if len(causal_links) > 0:
|
||||
# Verify link properties
|
||||
causal_types = {}
|
||||
for link in causal_links:
|
||||
link_type = link['link_type']
|
||||
causal_types[link_type] = causal_types.get(link_type, 0) + 1
|
||||
from_id = str(link['from_unit_id'])
|
||||
to_id = str(link['to_unit_id'])
|
||||
logger.info(f" Link: {from_id[:8]}... -> {to_id[:8]}... ({link_type}, weight: {link['weight']:.2f})")
|
||||
assert link['link_type'] in ['causes', 'caused_by', 'enables', 'prevents'], \
|
||||
f"Causal link type must be valid, got '{link['link_type']}'"
|
||||
assert 0.0 <= link['weight'] <= 1.0, "Weight should be between 0 and 1"
|
||||
|
||||
logger.info("Causal links created successfully:")
|
||||
for link_type, count in causal_types.items():
|
||||
logger.info(f" - {link_type}: {count} links")
|
||||
else:
|
||||
logger.warning("No causal links detected (LLM may not have extracted causal relationships)")
|
||||
logger.info(" This is expected as causal extraction depends on LLM interpretation")
|
||||
|
||||
# This test passes even if no causal links are found, since causal extraction
|
||||
# is non-deterministic and depends on LLM behavior
|
||||
logger.info("Test completed (causal link extraction is LLM-dependent)")
|
||||
|
||||
finally:
|
||||
await memory.delete_bank(bank_id)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_all_link_types_together(memory):
|
||||
"""
|
||||
Integration test: Verify all link types can be created in a single retain operation.
|
||||
|
||||
Tests that temporal, semantic, entity, and potentially causal links are all
|
||||
created when appropriate conditions are met.
|
||||
"""
|
||||
bank_id = f"test_all_links_{datetime.now(timezone.utc).timestamp()}"
|
||||
|
||||
try:
|
||||
# Store multiple related facts that should trigger all link types
|
||||
base_date = datetime(2024, 1, 15, 10, 0, 0, tzinfo=timezone.utc)
|
||||
|
||||
# Fact 1: Alice at time T
|
||||
unit_ids_1 = await memory.retain_async(
|
||||
bank_id=bank_id,
|
||||
content="Alice completed the Python backend service for the authentication system.",
|
||||
context="sprint review",
|
||||
event_date=base_date
|
||||
)
|
||||
|
||||
# Fact 2: Related to Alice, similar topic (Python), close in time
|
||||
unit_ids_2 = await memory.retain_async(
|
||||
bank_id=bank_id,
|
||||
content="Alice optimized the Python code and improved the authentication performance by 40%.",
|
||||
context="sprint review",
|
||||
event_date=base_date.replace(hour=14) # Same day, 4 hours later
|
||||
)
|
||||
|
||||
# Fact 3: Related to Alice, different topic but same entity
|
||||
unit_ids_3 = await memory.retain_async(
|
||||
bank_id=bank_id,
|
||||
content="Alice presented the security architecture at the team meeting.",
|
||||
context="team meeting",
|
||||
event_date=base_date.replace(day=16) # Next day
|
||||
)
|
||||
|
||||
assert len(unit_ids_1) > 0 and len(unit_ids_2) > 0 and len(unit_ids_3) > 0
|
||||
|
||||
logger.info(f"Created {len(unit_ids_1) + len(unit_ids_2) + len(unit_ids_3)} facts")
|
||||
|
||||
# Query for all link types
|
||||
async with memory._pool.acquire() as conn:
|
||||
all_unit_ids = unit_ids_1 + unit_ids_2 + unit_ids_3
|
||||
|
||||
all_links = await conn.fetch(
|
||||
"""
|
||||
SELECT link_type, COUNT(*) as count
|
||||
FROM memory_links
|
||||
WHERE from_unit_id::text = ANY($1)
|
||||
GROUP BY link_type
|
||||
ORDER BY link_type
|
||||
""",
|
||||
all_unit_ids
|
||||
)
|
||||
|
||||
logger.info("Link types created:")
|
||||
link_types_found = {}
|
||||
for row in all_links:
|
||||
link_type = row['link_type']
|
||||
count = row['count']
|
||||
link_types_found[link_type] = count
|
||||
logger.info(f" - {link_type}: {count} links")
|
||||
|
||||
# Should have temporal, semantic, and entity links
|
||||
assert 'temporal' in link_types_found, "Should have temporal links (facts with nearby dates)"
|
||||
assert 'semantic' in link_types_found, "Should have semantic links (similar content about Python/auth)"
|
||||
assert 'entity' in link_types_found, "Should have entity links (all mention Alice)"
|
||||
|
||||
logger.info(f"Successfully created {len(link_types_found)} different link types")
|
||||
logger.info("All major link types (temporal, semantic, entity) are working correctly")
|
||||
|
||||
finally:
|
||||
await memory.delete_bank(bank_id)
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ pub fn get(
|
|||
Ok(doc) => {
|
||||
if output_format == OutputFormat::Pretty {
|
||||
ui::print_info(&format!("Document: {}", doc.id));
|
||||
println!(" Agent ID: {}", doc.agent_id);
|
||||
println!(" Bank ID: {}", doc.bank_id);
|
||||
println!(" Created: {}", doc.created_at);
|
||||
println!(" Updated: {}", doc.updated_at);
|
||||
println!(" Memory Units: {}", doc.memory_unit_count);
|
||||
|
|
|
|||
|
|
@ -25,14 +25,14 @@ pub fn list(
|
|||
if output_format == OutputFormat::Pretty {
|
||||
ui::print_section_header(&format!("Entities for Bank: {}", bank_id));
|
||||
|
||||
if response.entities.is_empty() {
|
||||
if response.items.is_empty() {
|
||||
ui::print_warning("No entities found");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("Total entities: {}\n", response.entities.len());
|
||||
println!("Total entities: {}\n", response.items.len());
|
||||
|
||||
for entity in &response.entities {
|
||||
for entity in &response.items {
|
||||
println!("ID: {}", entity.id);
|
||||
println!(" Name: {}", entity.canonical_name);
|
||||
println!(" Mentions: {}", entity.mention_count);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -8,8 +8,8 @@ use crate::config;
|
|||
use crate::output::{self, OutputFormat};
|
||||
use crate::ui;
|
||||
|
||||
// Import Budget type from generated client
|
||||
use hindsight_client::types::Budget;
|
||||
// Import types from generated client
|
||||
use hindsight_client::types::{Budget, ChunkIncludeOptions, IncludeOptions};
|
||||
|
||||
// Helper function to parse budget string to Budget enum
|
||||
fn parse_budget(budget: &str) -> Budget {
|
||||
|
|
@ -28,6 +28,8 @@ pub fn recall(
|
|||
budget: String,
|
||||
max_tokens: i64,
|
||||
trace: bool,
|
||||
include_chunks: bool,
|
||||
chunk_max_tokens: i64,
|
||||
verbose: bool,
|
||||
output_format: OutputFormat,
|
||||
) -> Result<()> {
|
||||
|
|
@ -37,6 +39,18 @@ pub fn recall(
|
|||
None
|
||||
};
|
||||
|
||||
// Build include options if chunks are requested
|
||||
let include = if include_chunks {
|
||||
Some(IncludeOptions {
|
||||
chunks: Some(ChunkIncludeOptions {
|
||||
max_tokens: chunk_max_tokens,
|
||||
}),
|
||||
entities: None,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let request = RecallRequest {
|
||||
query,
|
||||
types: if fact_type.is_empty() { None } else { Some(fact_type) },
|
||||
|
|
@ -45,7 +59,7 @@ pub fn recall(
|
|||
trace,
|
||||
query_timestamp: None,
|
||||
filters: None,
|
||||
include: None,
|
||||
include,
|
||||
};
|
||||
|
||||
let response = client.recall(agent_id, &request, verbose);
|
||||
|
|
@ -57,7 +71,7 @@ pub fn recall(
|
|||
match response {
|
||||
Ok(result) => {
|
||||
if output_format == OutputFormat::Pretty {
|
||||
ui::print_search_results(&result, trace);
|
||||
ui::print_search_results(&result, trace, include_chunks);
|
||||
} else {
|
||||
output::print_output(&result, output_format)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,6 +160,14 @@ enum MemoryCommands {
|
|||
/// Show trace information
|
||||
#[arg(long)]
|
||||
trace: bool,
|
||||
|
||||
/// Include chunks in results
|
||||
#[arg(long)]
|
||||
include_chunks: bool,
|
||||
|
||||
/// Maximum tokens for chunks (only used with --include-chunks)
|
||||
#[arg(long, default_value = "8192")]
|
||||
chunk_max_tokens: i64,
|
||||
},
|
||||
|
||||
/// Generate answers using bank identity (reflect/reasoning)
|
||||
|
|
@ -379,8 +387,8 @@ fn run() -> Result<()> {
|
|||
},
|
||||
|
||||
Commands::Memory(memory_cmd) => match memory_cmd {
|
||||
MemoryCommands::Recall { bank_id, query, fact_type, budget, max_tokens, trace } => {
|
||||
commands::memory::recall(&client, &bank_id, query, fact_type, budget, max_tokens, trace, verbose, output_format)
|
||||
MemoryCommands::Recall { bank_id, query, fact_type, budget, max_tokens, trace, include_chunks, chunk_max_tokens } => {
|
||||
commands::memory::recall(&client, &bank_id, query, fact_type, budget, max_tokens, trace, include_chunks, chunk_max_tokens, verbose, output_format)
|
||||
}
|
||||
MemoryCommands::Reflect { bank_id, query, budget, context } => {
|
||||
commands::memory::reflect(&client, &bank_id, query, budget, context, verbose, output_format)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use crate::api::{BankProfileResponse, RecallResult, RecallResponse, ReflectResponse};
|
||||
use colored::*;
|
||||
use hindsight_client::types::ChunkData;
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
use std::io::{self, Write};
|
||||
|
||||
|
|
@ -60,7 +61,29 @@ pub fn print_fact(fact: &RecallResult, show_activation: bool) {
|
|||
println!();
|
||||
}
|
||||
|
||||
pub fn print_search_results(response: &RecallResponse, show_trace: bool) {
|
||||
pub fn print_chunk(chunk: &ChunkData) {
|
||||
println!(" {}", "─── Source Chunk ───".bright_blue());
|
||||
|
||||
// Split text into lines and indent each line
|
||||
for line in chunk.text.lines() {
|
||||
println!(" {}", line.bright_white());
|
||||
}
|
||||
|
||||
if chunk.truncated {
|
||||
println!(" {}", "[Truncated due to token limit]".bright_yellow());
|
||||
}
|
||||
|
||||
println!(" {}: {} | {}: {}",
|
||||
"Chunk ID".bright_black(),
|
||||
chunk.id.bright_black(),
|
||||
"Index".bright_black(),
|
||||
chunk.chunk_index.to_string().bright_black()
|
||||
);
|
||||
|
||||
println!();
|
||||
}
|
||||
|
||||
pub fn print_search_results(response: &RecallResponse, show_trace: bool, show_chunks: bool) {
|
||||
let results = &response.results;
|
||||
print_section_header(&format!("Search Results ({})", results.len()));
|
||||
|
||||
|
|
@ -70,6 +93,17 @@ pub fn print_search_results(response: &RecallResponse, show_trace: bool) {
|
|||
for (i, fact) in results.iter().enumerate() {
|
||||
println!("{}", format!(" Result #{}", i + 1).bright_black());
|
||||
print_fact(fact, true);
|
||||
|
||||
// Show chunk if available and requested
|
||||
if show_chunks {
|
||||
if let Some(chunk_id) = &fact.chunk_id {
|
||||
if let Some(chunks) = &response.chunks {
|
||||
if let Some(chunk) = chunks.get(chunk_id) {
|
||||
print_chunk(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
hindsight_client_api/__init__.py
|
||||
hindsight_client_api/api/__init__.py
|
||||
hindsight_client_api/api/default_api.py
|
||||
hindsight_client_api/api/monitoring_api.py
|
||||
hindsight_client_api/api_client.py
|
||||
hindsight_client_api/api_response.py
|
||||
hindsight_client_api/configuration.py
|
||||
|
|
@ -10,6 +11,8 @@ hindsight_client_api/docs/BankListItem.md
|
|||
hindsight_client_api/docs/BankListResponse.md
|
||||
hindsight_client_api/docs/BankProfileResponse.md
|
||||
hindsight_client_api/docs/Budget.md
|
||||
hindsight_client_api/docs/ChunkData.md
|
||||
hindsight_client_api/docs/ChunkIncludeOptions.md
|
||||
hindsight_client_api/docs/CreateBankRequest.md
|
||||
hindsight_client_api/docs/DefaultApi.md
|
||||
hindsight_client_api/docs/DeleteResponse.md
|
||||
|
|
@ -27,6 +30,7 @@ hindsight_client_api/docs/ListDocumentsResponse.md
|
|||
hindsight_client_api/docs/ListMemoryUnitsResponse.md
|
||||
hindsight_client_api/docs/MemoryItem.md
|
||||
hindsight_client_api/docs/MetadataFilter.md
|
||||
hindsight_client_api/docs/MonitoringApi.md
|
||||
hindsight_client_api/docs/PersonalityTraits.md
|
||||
hindsight_client_api/docs/RecallRequest.md
|
||||
hindsight_client_api/docs/RecallResponse.md
|
||||
|
|
@ -48,6 +52,8 @@ hindsight_client_api/models/bank_list_item.py
|
|||
hindsight_client_api/models/bank_list_response.py
|
||||
hindsight_client_api/models/bank_profile_response.py
|
||||
hindsight_client_api/models/budget.py
|
||||
hindsight_client_api/models/chunk_data.py
|
||||
hindsight_client_api/models/chunk_include_options.py
|
||||
hindsight_client_api/models/create_bank_request.py
|
||||
hindsight_client_api/models/delete_response.py
|
||||
hindsight_client_api/models/document_response.py
|
||||
|
|
@ -85,6 +91,8 @@ hindsight_client_api/test/test_bank_list_item.py
|
|||
hindsight_client_api/test/test_bank_list_response.py
|
||||
hindsight_client_api/test/test_bank_profile_response.py
|
||||
hindsight_client_api/test/test_budget.py
|
||||
hindsight_client_api/test/test_chunk_data.py
|
||||
hindsight_client_api/test/test_chunk_include_options.py
|
||||
hindsight_client_api/test/test_create_bank_request.py
|
||||
hindsight_client_api/test/test_default_api.py
|
||||
hindsight_client_api/test/test_delete_response.py
|
||||
|
|
@ -102,6 +110,7 @@ hindsight_client_api/test/test_list_documents_response.py
|
|||
hindsight_client_api/test/test_list_memory_units_response.py
|
||||
hindsight_client_api/test/test_memory_item.py
|
||||
hindsight_client_api/test/test_metadata_filter.py
|
||||
hindsight_client_api/test/test_monitoring_api.py
|
||||
hindsight_client_api/test/test_personality_traits.py
|
||||
hindsight_client_api/test/test_recall_request.py
|
||||
hindsight_client_api/test/test_recall_response.py
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
# Hindisight python client
|
||||
|
|
@ -18,6 +18,7 @@ __version__ = "0.0.7"
|
|||
|
||||
# Define package exports
|
||||
__all__ = [
|
||||
"MonitoringApi",
|
||||
"DefaultApi",
|
||||
"ApiResponse",
|
||||
"ApiClient",
|
||||
|
|
@ -34,6 +35,8 @@ __all__ = [
|
|||
"BankListResponse",
|
||||
"BankProfileResponse",
|
||||
"Budget",
|
||||
"ChunkData",
|
||||
"ChunkIncludeOptions",
|
||||
"CreateBankRequest",
|
||||
"DeleteResponse",
|
||||
"DocumentResponse",
|
||||
|
|
@ -66,6 +69,7 @@ __all__ = [
|
|||
]
|
||||
|
||||
# import apis into sdk package
|
||||
from hindsight_client_api.api.monitoring_api import MonitoringApi as MonitoringApi
|
||||
from hindsight_client_api.api.default_api import DefaultApi as DefaultApi
|
||||
|
||||
# import ApiClient
|
||||
|
|
@ -86,6 +90,8 @@ from hindsight_client_api.models.bank_list_item import BankListItem as BankListI
|
|||
from hindsight_client_api.models.bank_list_response import BankListResponse as BankListResponse
|
||||
from hindsight_client_api.models.bank_profile_response import BankProfileResponse as BankProfileResponse
|
||||
from hindsight_client_api.models.budget import Budget as Budget
|
||||
from hindsight_client_api.models.chunk_data import ChunkData as ChunkData
|
||||
from hindsight_client_api.models.chunk_include_options import ChunkIncludeOptions as ChunkIncludeOptions
|
||||
from hindsight_client_api.models.create_bank_request import CreateBankRequest as CreateBankRequest
|
||||
from hindsight_client_api.models.delete_response import DeleteResponse as DeleteResponse
|
||||
from hindsight_client_api.models.document_response import DocumentResponse as DocumentResponse
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
# flake8: noqa
|
||||
|
||||
# import apis into api package
|
||||
from hindsight_client_api.api.monitoring_api import MonitoringApi
|
||||
from hindsight_client_api.api.default_api import DefaultApi
|
||||
|
||||
|
|
|
|||
|
|
@ -3699,7 +3699,7 @@ class DefaultApi:
|
|||
) -> ListMemoryUnitsResponse:
|
||||
"""List memory units
|
||||
|
||||
List memory units with pagination and optional full-text search. Supports filtering by type.
|
||||
List memory units with pagination and optional full-text search. Supports filtering by type. Results are sorted by most recent first (mentioned_at DESC, then created_at DESC).
|
||||
|
||||
:param bank_id: (required)
|
||||
:type bank_id: str
|
||||
|
|
@ -3783,7 +3783,7 @@ class DefaultApi:
|
|||
) -> ApiResponse[ListMemoryUnitsResponse]:
|
||||
"""List memory units
|
||||
|
||||
List memory units with pagination and optional full-text search. Supports filtering by type.
|
||||
List memory units with pagination and optional full-text search. Supports filtering by type. Results are sorted by most recent first (mentioned_at DESC, then created_at DESC).
|
||||
|
||||
:param bank_id: (required)
|
||||
:type bank_id: str
|
||||
|
|
@ -3867,7 +3867,7 @@ class DefaultApi:
|
|||
) -> RESTResponseType:
|
||||
"""List memory units
|
||||
|
||||
List memory units with pagination and optional full-text search. Supports filtering by type.
|
||||
List memory units with pagination and optional full-text search. Supports filtering by type. Results are sorted by most recent first (mentioned_at DESC, then created_at DESC).
|
||||
|
||||
:param bank_id: (required)
|
||||
:type bank_id: str
|
||||
|
|
|
|||
|
|
@ -0,0 +1,281 @@
|
|||
# coding: utf-8
|
||||
|
||||
"""
|
||||
Hindsight HTTP API
|
||||
|
||||
HTTP API for Hindsight
|
||||
|
||||
The version of the OpenAPI document: 1.0.0
|
||||
Generated by OpenAPI Generator (https://openapi-generator.tech)
|
||||
|
||||
Do not edit the class manually.
|
||||
""" # noqa: E501
|
||||
|
||||
import warnings
|
||||
from pydantic import validate_call, Field, StrictFloat, StrictStr, StrictInt
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
from typing_extensions import Annotated
|
||||
|
||||
from typing import Any
|
||||
|
||||
from hindsight_client_api.api_client import ApiClient, RequestSerialized
|
||||
from hindsight_client_api.api_response import ApiResponse
|
||||
from hindsight_client_api.rest import RESTResponseType
|
||||
|
||||
|
||||
class MonitoringApi:
|
||||
"""NOTE: This class is auto generated by OpenAPI Generator
|
||||
Ref: https://openapi-generator.tech
|
||||
|
||||
Do not edit the class manually.
|
||||
"""
|
||||
|
||||
def __init__(self, api_client=None) -> None:
|
||||
if api_client is None:
|
||||
api_client = ApiClient.get_default()
|
||||
self.api_client = api_client
|
||||
|
||||
|
||||
@validate_call
|
||||
async def metrics_endpoint_metrics_get(
|
||||
self,
|
||||
_request_timeout: Union[
|
||||
None,
|
||||
Annotated[StrictFloat, Field(gt=0)],
|
||||
Tuple[
|
||||
Annotated[StrictFloat, Field(gt=0)],
|
||||
Annotated[StrictFloat, Field(gt=0)]
|
||||
]
|
||||
] = None,
|
||||
_request_auth: Optional[Dict[StrictStr, Any]] = None,
|
||||
_content_type: Optional[StrictStr] = None,
|
||||
_headers: Optional[Dict[StrictStr, Any]] = None,
|
||||
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
|
||||
) -> object:
|
||||
"""Prometheus metrics endpoint
|
||||
|
||||
Exports metrics in Prometheus format for scraping
|
||||
|
||||
:param _request_timeout: timeout setting for this request. If one
|
||||
number provided, it will be total request
|
||||
timeout. It can also be a pair (tuple) of
|
||||
(connection, read) timeouts.
|
||||
:type _request_timeout: int, tuple(int, int), optional
|
||||
:param _request_auth: set to override the auth_settings for an a single
|
||||
request; this effectively ignores the
|
||||
authentication in the spec for a single request.
|
||||
:type _request_auth: dict, optional
|
||||
:param _content_type: force content-type for the request.
|
||||
:type _content_type: str, Optional
|
||||
:param _headers: set to override the headers for a single
|
||||
request; this effectively ignores the headers
|
||||
in the spec for a single request.
|
||||
:type _headers: dict, optional
|
||||
:param _host_index: set to override the host_index for a single
|
||||
request; this effectively ignores the host_index
|
||||
in the spec for a single request.
|
||||
:type _host_index: int, optional
|
||||
:return: Returns the result object.
|
||||
""" # noqa: E501
|
||||
|
||||
_param = self._metrics_endpoint_metrics_get_serialize(
|
||||
_request_auth=_request_auth,
|
||||
_content_type=_content_type,
|
||||
_headers=_headers,
|
||||
_host_index=_host_index
|
||||
)
|
||||
|
||||
_response_types_map: Dict[str, Optional[str]] = {
|
||||
'200': "object",
|
||||
}
|
||||
response_data = await self.api_client.call_api(
|
||||
*_param,
|
||||
_request_timeout=_request_timeout
|
||||
)
|
||||
await response_data.read()
|
||||
return self.api_client.response_deserialize(
|
||||
response_data=response_data,
|
||||
response_types_map=_response_types_map,
|
||||
).data
|
||||
|
||||
|
||||
@validate_call
|
||||
async def metrics_endpoint_metrics_get_with_http_info(
|
||||
self,
|
||||
_request_timeout: Union[
|
||||
None,
|
||||
Annotated[StrictFloat, Field(gt=0)],
|
||||
Tuple[
|
||||
Annotated[StrictFloat, Field(gt=0)],
|
||||
Annotated[StrictFloat, Field(gt=0)]
|
||||
]
|
||||
] = None,
|
||||
_request_auth: Optional[Dict[StrictStr, Any]] = None,
|
||||
_content_type: Optional[StrictStr] = None,
|
||||
_headers: Optional[Dict[StrictStr, Any]] = None,
|
||||
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
|
||||
) -> ApiResponse[object]:
|
||||
"""Prometheus metrics endpoint
|
||||
|
||||
Exports metrics in Prometheus format for scraping
|
||||
|
||||
:param _request_timeout: timeout setting for this request. If one
|
||||
number provided, it will be total request
|
||||
timeout. It can also be a pair (tuple) of
|
||||
(connection, read) timeouts.
|
||||
:type _request_timeout: int, tuple(int, int), optional
|
||||
:param _request_auth: set to override the auth_settings for an a single
|
||||
request; this effectively ignores the
|
||||
authentication in the spec for a single request.
|
||||
:type _request_auth: dict, optional
|
||||
:param _content_type: force content-type for the request.
|
||||
:type _content_type: str, Optional
|
||||
:param _headers: set to override the headers for a single
|
||||
request; this effectively ignores the headers
|
||||
in the spec for a single request.
|
||||
:type _headers: dict, optional
|
||||
:param _host_index: set to override the host_index for a single
|
||||
request; this effectively ignores the host_index
|
||||
in the spec for a single request.
|
||||
:type _host_index: int, optional
|
||||
:return: Returns the result object.
|
||||
""" # noqa: E501
|
||||
|
||||
_param = self._metrics_endpoint_metrics_get_serialize(
|
||||
_request_auth=_request_auth,
|
||||
_content_type=_content_type,
|
||||
_headers=_headers,
|
||||
_host_index=_host_index
|
||||
)
|
||||
|
||||
_response_types_map: Dict[str, Optional[str]] = {
|
||||
'200': "object",
|
||||
}
|
||||
response_data = await self.api_client.call_api(
|
||||
*_param,
|
||||
_request_timeout=_request_timeout
|
||||
)
|
||||
await response_data.read()
|
||||
return self.api_client.response_deserialize(
|
||||
response_data=response_data,
|
||||
response_types_map=_response_types_map,
|
||||
)
|
||||
|
||||
|
||||
@validate_call
|
||||
async def metrics_endpoint_metrics_get_without_preload_content(
|
||||
self,
|
||||
_request_timeout: Union[
|
||||
None,
|
||||
Annotated[StrictFloat, Field(gt=0)],
|
||||
Tuple[
|
||||
Annotated[StrictFloat, Field(gt=0)],
|
||||
Annotated[StrictFloat, Field(gt=0)]
|
||||
]
|
||||
] = None,
|
||||
_request_auth: Optional[Dict[StrictStr, Any]] = None,
|
||||
_content_type: Optional[StrictStr] = None,
|
||||
_headers: Optional[Dict[StrictStr, Any]] = None,
|
||||
_host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
|
||||
) -> RESTResponseType:
|
||||
"""Prometheus metrics endpoint
|
||||
|
||||
Exports metrics in Prometheus format for scraping
|
||||
|
||||
:param _request_timeout: timeout setting for this request. If one
|
||||
number provided, it will be total request
|
||||
timeout. It can also be a pair (tuple) of
|
||||
(connection, read) timeouts.
|
||||
:type _request_timeout: int, tuple(int, int), optional
|
||||
:param _request_auth: set to override the auth_settings for an a single
|
||||
request; this effectively ignores the
|
||||
authentication in the spec for a single request.
|
||||
:type _request_auth: dict, optional
|
||||
:param _content_type: force content-type for the request.
|
||||
:type _content_type: str, Optional
|
||||
:param _headers: set to override the headers for a single
|
||||
request; this effectively ignores the headers
|
||||
in the spec for a single request.
|
||||
:type _headers: dict, optional
|
||||
:param _host_index: set to override the host_index for a single
|
||||
request; this effectively ignores the host_index
|
||||
in the spec for a single request.
|
||||
:type _host_index: int, optional
|
||||
:return: Returns the result object.
|
||||
""" # noqa: E501
|
||||
|
||||
_param = self._metrics_endpoint_metrics_get_serialize(
|
||||
_request_auth=_request_auth,
|
||||
_content_type=_content_type,
|
||||
_headers=_headers,
|
||||
_host_index=_host_index
|
||||
)
|
||||
|
||||
_response_types_map: Dict[str, Optional[str]] = {
|
||||
'200': "object",
|
||||
}
|
||||
response_data = await self.api_client.call_api(
|
||||
*_param,
|
||||
_request_timeout=_request_timeout
|
||||
)
|
||||
return response_data.response
|
||||
|
||||
|
||||
def _metrics_endpoint_metrics_get_serialize(
|
||||
self,
|
||||
_request_auth,
|
||||
_content_type,
|
||||
_headers,
|
||||
_host_index,
|
||||
) -> RequestSerialized:
|
||||
|
||||
_host = None
|
||||
|
||||
_collection_formats: Dict[str, str] = {
|
||||
}
|
||||
|
||||
_path_params: Dict[str, str] = {}
|
||||
_query_params: List[Tuple[str, str]] = []
|
||||
_header_params: Dict[str, Optional[str]] = _headers or {}
|
||||
_form_params: List[Tuple[str, str]] = []
|
||||
_files: Dict[
|
||||
str, Union[str, bytes, List[str], List[bytes], List[Tuple[str, bytes]]]
|
||||
] = {}
|
||||
_body_params: Optional[bytes] = None
|
||||
|
||||
# process the path parameters
|
||||
# process the query parameters
|
||||
# process the header parameters
|
||||
# process the form parameters
|
||||
# process the body parameter
|
||||
|
||||
|
||||
# set the HTTP header `Accept`
|
||||
if 'Accept' not in _header_params:
|
||||
_header_params['Accept'] = self.api_client.select_header_accept(
|
||||
[
|
||||
'application/json'
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
# authentication setting
|
||||
_auth_settings: List[str] = [
|
||||
]
|
||||
|
||||
return self.api_client.param_serialize(
|
||||
method='GET',
|
||||
resource_path='/metrics',
|
||||
path_params=_path_params,
|
||||
query_params=_query_params,
|
||||
header_params=_header_params,
|
||||
body=_body_params,
|
||||
post_params=_form_params,
|
||||
files=_files,
|
||||
auth_settings=_auth_settings,
|
||||
collection_formats=_collection_formats,
|
||||
_host=_host,
|
||||
_request_auth=_request_auth
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# ChunkData
|
||||
|
||||
Chunk data for a single chunk.
|
||||
|
||||
## Properties
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**id** | **str** | |
|
||||
**text** | **str** | |
|
||||
**chunk_index** | **int** | |
|
||||
**truncated** | **bool** | Whether the chunk text was truncated due to token limits | [optional] [default to False]
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from hindsight_client_api.models.chunk_data import ChunkData
|
||||
|
||||
# TODO update the JSON string below
|
||||
json = "{}"
|
||||
# create an instance of ChunkData from a JSON string
|
||||
chunk_data_instance = ChunkData.from_json(json)
|
||||
# print the JSON string representation of the object
|
||||
print(ChunkData.to_json())
|
||||
|
||||
# convert the object into a dict
|
||||
chunk_data_dict = chunk_data_instance.to_dict()
|
||||
# create an instance of ChunkData from a dict
|
||||
chunk_data_from_dict = ChunkData.from_dict(chunk_data_dict)
|
||||
```
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# ChunkIncludeOptions
|
||||
|
||||
Options for including chunks in recall results.
|
||||
|
||||
## Properties
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**max_tokens** | **int** | Maximum tokens for chunks (chunks may be truncated) | [optional] [default to 8192]
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from hindsight_client_api.models.chunk_include_options import ChunkIncludeOptions
|
||||
|
||||
# TODO update the JSON string below
|
||||
json = "{}"
|
||||
# create an instance of ChunkIncludeOptions from a JSON string
|
||||
chunk_include_options_instance = ChunkIncludeOptions.from_json(json)
|
||||
# print the JSON string representation of the object
|
||||
print(ChunkIncludeOptions.to_json())
|
||||
|
||||
# convert the object into a dict
|
||||
chunk_include_options_dict = chunk_include_options_instance.to_dict()
|
||||
# create an instance of ChunkIncludeOptions from a dict
|
||||
chunk_include_options_from_dict = ChunkIncludeOptions.from_dict(chunk_include_options_dict)
|
||||
```
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
|
|
@ -953,7 +953,7 @@ No authorization required
|
|||
|
||||
List memory units
|
||||
|
||||
List memory units with pagination and optional full-text search. Supports filtering by type.
|
||||
List memory units with pagination and optional full-text search. Supports filtering by type. Results are sorted by most recent first (mentioned_at DESC, then created_at DESC).
|
||||
|
||||
### Example
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ Response model for get document endpoint.
|
|||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**id** | **str** | |
|
||||
**agent_id** | **str** | |
|
||||
**bank_id** | **str** | |
|
||||
**original_text** | **str** | |
|
||||
**content_hash** | **str** | |
|
||||
**created_at** | **str** | |
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Response model for entity list endpoint.
|
|||
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**entities** | [**List[EntityListItem]**](EntityListItem.md) | |
|
||||
**items** | [**List[EntityListItem]**](EntityListItem.md) | |
|
||||
|
||||
## Example
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ Options for including additional data in recall results.
|
|||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**entities** | [**EntityIncludeOptions**](EntityIncludeOptions.md) | | [optional]
|
||||
**chunks** | [**ChunkIncludeOptions**](ChunkIncludeOptions.md) | | [optional]
|
||||
|
||||
## Example
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
# hindsight_client_api.MonitoringApi
|
||||
|
||||
All URIs are relative to *http://localhost*
|
||||
|
||||
Method | HTTP request | Description
|
||||
------------- | ------------- | -------------
|
||||
[**metrics_endpoint_metrics_get**](MonitoringApi.md#metrics_endpoint_metrics_get) | **GET** /metrics | Prometheus metrics endpoint
|
||||
|
||||
|
||||
# **metrics_endpoint_metrics_get**
|
||||
> object metrics_endpoint_metrics_get()
|
||||
|
||||
Prometheus metrics endpoint
|
||||
|
||||
Exports metrics in Prometheus format for scraping
|
||||
|
||||
### Example
|
||||
|
||||
|
||||
```python
|
||||
import hindsight_client_api
|
||||
from hindsight_client_api.rest import ApiException
|
||||
from pprint import pprint
|
||||
|
||||
# Defining the host is optional and defaults to http://localhost
|
||||
# See configuration.py for a list of all supported configuration parameters.
|
||||
configuration = hindsight_client_api.Configuration(
|
||||
host = "http://localhost"
|
||||
)
|
||||
|
||||
|
||||
# Enter a context with an instance of the API client
|
||||
async with hindsight_client_api.ApiClient(configuration) as api_client:
|
||||
# Create an instance of the API class
|
||||
api_instance = hindsight_client_api.MonitoringApi(api_client)
|
||||
|
||||
try:
|
||||
# Prometheus metrics endpoint
|
||||
api_response = await api_instance.metrics_endpoint_metrics_get()
|
||||
print("The response of MonitoringApi->metrics_endpoint_metrics_get:\n")
|
||||
pprint(api_response)
|
||||
except Exception as e:
|
||||
print("Exception when calling MonitoringApi->metrics_endpoint_metrics_get: %s\n" % e)
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Parameters
|
||||
|
||||
This endpoint does not need any parameter.
|
||||
|
||||
### Return type
|
||||
|
||||
**object**
|
||||
|
||||
### Authorization
|
||||
|
||||
No authorization required
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: Not defined
|
||||
- **Accept**: application/json
|
||||
|
||||
### HTTP response details
|
||||
|
||||
| Status code | Description | Response headers |
|
||||
|-------------|-------------|------------------|
|
||||
**200** | Successful Response | - |
|
||||
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
|
|
@ -9,6 +9,7 @@ Name | Type | Description | Notes
|
|||
**results** | [**List[RecallResult]**](RecallResult.md) | |
|
||||
**trace** | **Dict[str, object]** | | [optional]
|
||||
**entities** | [**Dict[str, EntityStateResponse]**](EntityStateResponse.md) | | [optional]
|
||||
**chunks** | [**Dict[str, ChunkData]**](ChunkData.md) | | [optional]
|
||||
|
||||
## Example
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ Name | Type | Description | Notes
|
|||
**mentioned_at** | **str** | | [optional]
|
||||
**document_id** | **str** | | [optional]
|
||||
**metadata** | **Dict[str, str]** | | [optional]
|
||||
**chunk_id** | **str** | | [optional]
|
||||
|
||||
## Example
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ from hindsight_client_api.models.bank_list_item import BankListItem
|
|||
from hindsight_client_api.models.bank_list_response import BankListResponse
|
||||
from hindsight_client_api.models.bank_profile_response import BankProfileResponse
|
||||
from hindsight_client_api.models.budget import Budget
|
||||
from hindsight_client_api.models.chunk_data import ChunkData
|
||||
from hindsight_client_api.models.chunk_include_options import ChunkIncludeOptions
|
||||
from hindsight_client_api.models.create_bank_request import CreateBankRequest
|
||||
from hindsight_client_api.models.delete_response import DeleteResponse
|
||||
from hindsight_client_api.models.document_response import DocumentResponse
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
# coding: utf-8
|
||||
|
||||
"""
|
||||
Hindsight HTTP API
|
||||
|
||||
HTTP API for Hindsight
|
||||
|
||||
The version of the OpenAPI document: 1.0.0
|
||||
Generated by OpenAPI Generator (https://openapi-generator.tech)
|
||||
|
||||
Do not edit the class manually.
|
||||
""" # noqa: E501
|
||||
|
||||
|
||||
from __future__ import annotations
|
||||
import pprint
|
||||
import re # noqa: F401
|
||||
import json
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictInt, StrictStr
|
||||
from typing import Any, ClassVar, Dict, List, Optional
|
||||
from typing import Optional, Set
|
||||
from typing_extensions import Self
|
||||
|
||||
class ChunkData(BaseModel):
|
||||
"""
|
||||
Chunk data for a single chunk.
|
||||
""" # noqa: E501
|
||||
id: StrictStr
|
||||
text: StrictStr
|
||||
chunk_index: StrictInt
|
||||
truncated: Optional[StrictBool] = Field(default=False, description="Whether the chunk text was truncated due to token limits")
|
||||
__properties: ClassVar[List[str]] = ["id", "text", "chunk_index", "truncated"]
|
||||
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
validate_assignment=True,
|
||||
protected_namespaces=(),
|
||||
)
|
||||
|
||||
|
||||
def to_str(self) -> str:
|
||||
"""Returns the string representation of the model using alias"""
|
||||
return pprint.pformat(self.model_dump(by_alias=True))
|
||||
|
||||
def to_json(self) -> str:
|
||||
"""Returns the JSON representation of the model using alias"""
|
||||
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
|
||||
return json.dumps(self.to_dict())
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json_str: str) -> Optional[Self]:
|
||||
"""Create an instance of ChunkData from a JSON string"""
|
||||
return cls.from_dict(json.loads(json_str))
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Return the dictionary representation of the model using alias.
|
||||
|
||||
This has the following differences from calling pydantic's
|
||||
`self.model_dump(by_alias=True)`:
|
||||
|
||||
* `None` is only added to the output dict for nullable fields that
|
||||
were set at model initialization. Other fields with value `None`
|
||||
are ignored.
|
||||
"""
|
||||
excluded_fields: Set[str] = set([
|
||||
])
|
||||
|
||||
_dict = self.model_dump(
|
||||
by_alias=True,
|
||||
exclude=excluded_fields,
|
||||
exclude_none=True,
|
||||
)
|
||||
return _dict
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
|
||||
"""Create an instance of ChunkData from a dict"""
|
||||
if obj is None:
|
||||
return None
|
||||
|
||||
if not isinstance(obj, dict):
|
||||
return cls.model_validate(obj)
|
||||
|
||||
_obj = cls.model_validate({
|
||||
"id": obj.get("id"),
|
||||
"text": obj.get("text"),
|
||||
"chunk_index": obj.get("chunk_index"),
|
||||
"truncated": obj.get("truncated") if obj.get("truncated") is not None else False
|
||||
})
|
||||
return _obj
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
# coding: utf-8
|
||||
|
||||
"""
|
||||
Hindsight HTTP API
|
||||
|
||||
HTTP API for Hindsight
|
||||
|
||||
The version of the OpenAPI document: 1.0.0
|
||||
Generated by OpenAPI Generator (https://openapi-generator.tech)
|
||||
|
||||
Do not edit the class manually.
|
||||
""" # noqa: E501
|
||||
|
||||
|
||||
from __future__ import annotations
|
||||
import pprint
|
||||
import re # noqa: F401
|
||||
import json
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, StrictInt
|
||||
from typing import Any, ClassVar, Dict, List, Optional
|
||||
from typing import Optional, Set
|
||||
from typing_extensions import Self
|
||||
|
||||
class ChunkIncludeOptions(BaseModel):
|
||||
"""
|
||||
Options for including chunks in recall results.
|
||||
""" # noqa: E501
|
||||
max_tokens: Optional[StrictInt] = Field(default=8192, description="Maximum tokens for chunks (chunks may be truncated)")
|
||||
__properties: ClassVar[List[str]] = ["max_tokens"]
|
||||
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
validate_assignment=True,
|
||||
protected_namespaces=(),
|
||||
)
|
||||
|
||||
|
||||
def to_str(self) -> str:
|
||||
"""Returns the string representation of the model using alias"""
|
||||
return pprint.pformat(self.model_dump(by_alias=True))
|
||||
|
||||
def to_json(self) -> str:
|
||||
"""Returns the JSON representation of the model using alias"""
|
||||
# TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
|
||||
return json.dumps(self.to_dict())
|
||||
|
||||
@classmethod
|
||||
def from_json(cls, json_str: str) -> Optional[Self]:
|
||||
"""Create an instance of ChunkIncludeOptions from a JSON string"""
|
||||
return cls.from_dict(json.loads(json_str))
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Return the dictionary representation of the model using alias.
|
||||
|
||||
This has the following differences from calling pydantic's
|
||||
`self.model_dump(by_alias=True)`:
|
||||
|
||||
* `None` is only added to the output dict for nullable fields that
|
||||
were set at model initialization. Other fields with value `None`
|
||||
are ignored.
|
||||
"""
|
||||
excluded_fields: Set[str] = set([
|
||||
])
|
||||
|
||||
_dict = self.model_dump(
|
||||
by_alias=True,
|
||||
exclude=excluded_fields,
|
||||
exclude_none=True,
|
||||
)
|
||||
return _dict
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
|
||||
"""Create an instance of ChunkIncludeOptions from a dict"""
|
||||
if obj is None:
|
||||
return None
|
||||
|
||||
if not isinstance(obj, dict):
|
||||
return cls.model_validate(obj)
|
||||
|
||||
_obj = cls.model_validate({
|
||||
"max_tokens": obj.get("max_tokens") if obj.get("max_tokens") is not None else 8192
|
||||
})
|
||||
return _obj
|
||||
|
||||
|
||||
|
|
@ -27,13 +27,13 @@ class DocumentResponse(BaseModel):
|
|||
Response model for get document endpoint.
|
||||
""" # noqa: E501
|
||||
id: StrictStr
|
||||
agent_id: StrictStr
|
||||
bank_id: StrictStr
|
||||
original_text: StrictStr
|
||||
content_hash: Optional[StrictStr]
|
||||
created_at: StrictStr
|
||||
updated_at: StrictStr
|
||||
memory_unit_count: StrictInt
|
||||
__properties: ClassVar[List[str]] = ["id", "agent_id", "original_text", "content_hash", "created_at", "updated_at", "memory_unit_count"]
|
||||
__properties: ClassVar[List[str]] = ["id", "bank_id", "original_text", "content_hash", "created_at", "updated_at", "memory_unit_count"]
|
||||
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
|
|
@ -92,7 +92,7 @@ class DocumentResponse(BaseModel):
|
|||
|
||||
_obj = cls.model_validate({
|
||||
"id": obj.get("id"),
|
||||
"agent_id": obj.get("agent_id"),
|
||||
"bank_id": obj.get("bank_id"),
|
||||
"original_text": obj.get("original_text"),
|
||||
"content_hash": obj.get("content_hash"),
|
||||
"created_at": obj.get("created_at"),
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ class EntityListResponse(BaseModel):
|
|||
"""
|
||||
Response model for entity list endpoint.
|
||||
""" # noqa: E501
|
||||
entities: List[EntityListItem]
|
||||
__properties: ClassVar[List[str]] = ["entities"]
|
||||
items: List[EntityListItem]
|
||||
__properties: ClassVar[List[str]] = ["items"]
|
||||
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
|
|
@ -69,13 +69,13 @@ class EntityListResponse(BaseModel):
|
|||
exclude=excluded_fields,
|
||||
exclude_none=True,
|
||||
)
|
||||
# override the default output from pydantic by calling `to_dict()` of each item in entities (list)
|
||||
# override the default output from pydantic by calling `to_dict()` of each item in items (list)
|
||||
_items = []
|
||||
if self.entities:
|
||||
for _item_entities in self.entities:
|
||||
if _item_entities:
|
||||
_items.append(_item_entities.to_dict())
|
||||
_dict['entities'] = _items
|
||||
if self.items:
|
||||
for _item_items in self.items:
|
||||
if _item_items:
|
||||
_items.append(_item_items.to_dict())
|
||||
_dict['items'] = _items
|
||||
return _dict
|
||||
|
||||
@classmethod
|
||||
|
|
@ -88,7 +88,7 @@ class EntityListResponse(BaseModel):
|
|||
return cls.model_validate(obj)
|
||||
|
||||
_obj = cls.model_validate({
|
||||
"entities": [EntityListItem.from_dict(_item) for _item in obj["entities"]] if obj.get("entities") is not None else None
|
||||
"items": [EntityListItem.from_dict(_item) for _item in obj["items"]] if obj.get("items") is not None else None
|
||||
})
|
||||
return _obj
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import json
|
|||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from typing import Any, ClassVar, Dict, List, Optional
|
||||
from hindsight_client_api.models.chunk_include_options import ChunkIncludeOptions
|
||||
from hindsight_client_api.models.entity_include_options import EntityIncludeOptions
|
||||
from typing import Optional, Set
|
||||
from typing_extensions import Self
|
||||
|
|
@ -28,7 +29,8 @@ class IncludeOptions(BaseModel):
|
|||
Options for including additional data in recall results.
|
||||
""" # noqa: E501
|
||||
entities: Optional[EntityIncludeOptions] = None
|
||||
__properties: ClassVar[List[str]] = ["entities"]
|
||||
chunks: Optional[ChunkIncludeOptions] = None
|
||||
__properties: ClassVar[List[str]] = ["entities", "chunks"]
|
||||
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
|
|
@ -72,11 +74,19 @@ class IncludeOptions(BaseModel):
|
|||
# override the default output from pydantic by calling `to_dict()` of entities
|
||||
if self.entities:
|
||||
_dict['entities'] = self.entities.to_dict()
|
||||
# override the default output from pydantic by calling `to_dict()` of chunks
|
||||
if self.chunks:
|
||||
_dict['chunks'] = self.chunks.to_dict()
|
||||
# set to None if entities (nullable) is None
|
||||
# and model_fields_set contains the field
|
||||
if self.entities is None and "entities" in self.model_fields_set:
|
||||
_dict['entities'] = None
|
||||
|
||||
# set to None if chunks (nullable) is None
|
||||
# and model_fields_set contains the field
|
||||
if self.chunks is None and "chunks" in self.model_fields_set:
|
||||
_dict['chunks'] = None
|
||||
|
||||
return _dict
|
||||
|
||||
@classmethod
|
||||
|
|
@ -89,7 +99,8 @@ class IncludeOptions(BaseModel):
|
|||
return cls.model_validate(obj)
|
||||
|
||||
_obj = cls.model_validate({
|
||||
"entities": EntityIncludeOptions.from_dict(obj["entities"]) if obj.get("entities") is not None else None
|
||||
"entities": EntityIncludeOptions.from_dict(obj["entities"]) if obj.get("entities") is not None else None,
|
||||
"chunks": ChunkIncludeOptions.from_dict(obj["chunks"]) if obj.get("chunks") is not None else None
|
||||
})
|
||||
return _obj
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import json
|
|||
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
from typing import Any, ClassVar, Dict, List, Optional
|
||||
from hindsight_client_api.models.chunk_data import ChunkData
|
||||
from hindsight_client_api.models.entity_state_response import EntityStateResponse
|
||||
from hindsight_client_api.models.recall_result import RecallResult
|
||||
from typing import Optional, Set
|
||||
|
|
@ -31,7 +32,8 @@ class RecallResponse(BaseModel):
|
|||
results: List[RecallResult]
|
||||
trace: Optional[Dict[str, Any]] = None
|
||||
entities: Optional[Dict[str, EntityStateResponse]] = None
|
||||
__properties: ClassVar[List[str]] = ["results", "trace", "entities"]
|
||||
chunks: Optional[Dict[str, ChunkData]] = None
|
||||
__properties: ClassVar[List[str]] = ["results", "trace", "entities", "chunks"]
|
||||
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
|
|
@ -86,6 +88,13 @@ class RecallResponse(BaseModel):
|
|||
if self.entities[_key_entities]:
|
||||
_field_dict[_key_entities] = self.entities[_key_entities].to_dict()
|
||||
_dict['entities'] = _field_dict
|
||||
# override the default output from pydantic by calling `to_dict()` of each value in chunks (dict)
|
||||
_field_dict = {}
|
||||
if self.chunks:
|
||||
for _key_chunks in self.chunks:
|
||||
if self.chunks[_key_chunks]:
|
||||
_field_dict[_key_chunks] = self.chunks[_key_chunks].to_dict()
|
||||
_dict['chunks'] = _field_dict
|
||||
# set to None if trace (nullable) is None
|
||||
# and model_fields_set contains the field
|
||||
if self.trace is None and "trace" in self.model_fields_set:
|
||||
|
|
@ -96,6 +105,11 @@ class RecallResponse(BaseModel):
|
|||
if self.entities is None and "entities" in self.model_fields_set:
|
||||
_dict['entities'] = None
|
||||
|
||||
# set to None if chunks (nullable) is None
|
||||
# and model_fields_set contains the field
|
||||
if self.chunks is None and "chunks" in self.model_fields_set:
|
||||
_dict['chunks'] = None
|
||||
|
||||
return _dict
|
||||
|
||||
@classmethod
|
||||
|
|
@ -115,6 +129,12 @@ class RecallResponse(BaseModel):
|
|||
for _k, _v in obj["entities"].items()
|
||||
)
|
||||
if obj.get("entities") is not None
|
||||
else None,
|
||||
"chunks": dict(
|
||||
(_k, ChunkData.from_dict(_v))
|
||||
for _k, _v in obj["chunks"].items()
|
||||
)
|
||||
if obj.get("chunks") is not None
|
||||
else None
|
||||
})
|
||||
return _obj
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ class RecallResult(BaseModel):
|
|||
mentioned_at: Optional[StrictStr] = None
|
||||
document_id: Optional[StrictStr] = None
|
||||
metadata: Optional[Dict[str, StrictStr]] = None
|
||||
__properties: ClassVar[List[str]] = ["id", "text", "type", "entities", "context", "occurred_start", "occurred_end", "mentioned_at", "document_id", "metadata"]
|
||||
chunk_id: Optional[StrictStr] = None
|
||||
__properties: ClassVar[List[str]] = ["id", "text", "type", "entities", "context", "occurred_start", "occurred_end", "mentioned_at", "document_id", "metadata", "chunk_id"]
|
||||
|
||||
model_config = ConfigDict(
|
||||
populate_by_name=True,
|
||||
|
|
@ -117,6 +118,11 @@ class RecallResult(BaseModel):
|
|||
if self.metadata is None and "metadata" in self.model_fields_set:
|
||||
_dict['metadata'] = None
|
||||
|
||||
# set to None if chunk_id (nullable) is None
|
||||
# and model_fields_set contains the field
|
||||
if self.chunk_id is None and "chunk_id" in self.model_fields_set:
|
||||
_dict['chunk_id'] = None
|
||||
|
||||
return _dict
|
||||
|
||||
@classmethod
|
||||
|
|
@ -138,7 +144,8 @@ class RecallResult(BaseModel):
|
|||
"occurred_end": obj.get("occurred_end"),
|
||||
"mentioned_at": obj.get("mentioned_at"),
|
||||
"document_id": obj.get("document_id"),
|
||||
"metadata": obj.get("metadata")
|
||||
"metadata": obj.get("metadata"),
|
||||
"chunk_id": obj.get("chunk_id")
|
||||
})
|
||||
return _obj
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
# coding: utf-8
|
||||
|
||||
"""
|
||||
Hindsight HTTP API
|
||||
|
||||
HTTP API for Hindsight
|
||||
|
||||
The version of the OpenAPI document: 1.0.0
|
||||
Generated by OpenAPI Generator (https://openapi-generator.tech)
|
||||
|
||||
Do not edit the class manually.
|
||||
""" # noqa: E501
|
||||
|
||||
|
||||
import unittest
|
||||
|
||||
from hindsight_client_api.models.chunk_data import ChunkData
|
||||
|
||||
class TestChunkData(unittest.TestCase):
|
||||
"""ChunkData unit test stubs"""
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def make_instance(self, include_optional) -> ChunkData:
|
||||
"""Test ChunkData
|
||||
include_optional is a boolean, when False only required
|
||||
params are included, when True both required and
|
||||
optional params are included """
|
||||
# uncomment below to create an instance of `ChunkData`
|
||||
"""
|
||||
model = ChunkData()
|
||||
if include_optional:
|
||||
return ChunkData(
|
||||
id = '',
|
||||
text = '',
|
||||
chunk_index = 56,
|
||||
truncated = True
|
||||
)
|
||||
else:
|
||||
return ChunkData(
|
||||
id = '',
|
||||
text = '',
|
||||
chunk_index = 56,
|
||||
)
|
||||
"""
|
||||
|
||||
def testChunkData(self):
|
||||
"""Test ChunkData"""
|
||||
# inst_req_only = self.make_instance(include_optional=False)
|
||||
# inst_req_and_optional = self.make_instance(include_optional=True)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# coding: utf-8
|
||||
|
||||
"""
|
||||
Hindsight HTTP API
|
||||
|
||||
HTTP API for Hindsight
|
||||
|
||||
The version of the OpenAPI document: 1.0.0
|
||||
Generated by OpenAPI Generator (https://openapi-generator.tech)
|
||||
|
||||
Do not edit the class manually.
|
||||
""" # noqa: E501
|
||||
|
||||
|
||||
import unittest
|
||||
|
||||
from hindsight_client_api.models.chunk_include_options import ChunkIncludeOptions
|
||||
|
||||
class TestChunkIncludeOptions(unittest.TestCase):
|
||||
"""ChunkIncludeOptions unit test stubs"""
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
def make_instance(self, include_optional) -> ChunkIncludeOptions:
|
||||
"""Test ChunkIncludeOptions
|
||||
include_optional is a boolean, when False only required
|
||||
params are included, when True both required and
|
||||
optional params are included """
|
||||
# uncomment below to create an instance of `ChunkIncludeOptions`
|
||||
"""
|
||||
model = ChunkIncludeOptions()
|
||||
if include_optional:
|
||||
return ChunkIncludeOptions(
|
||||
max_tokens = 56
|
||||
)
|
||||
else:
|
||||
return ChunkIncludeOptions(
|
||||
)
|
||||
"""
|
||||
|
||||
def testChunkIncludeOptions(self):
|
||||
"""Test ChunkIncludeOptions"""
|
||||
# inst_req_only = self.make_instance(include_optional=False)
|
||||
# inst_req_and_optional = self.make_instance(include_optional=True)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
@ -36,7 +36,7 @@ class TestDocumentResponse(unittest.TestCase):
|
|||
if include_optional:
|
||||
return DocumentResponse(
|
||||
id = '',
|
||||
agent_id = '',
|
||||
bank_id = '',
|
||||
original_text = '',
|
||||
content_hash = '',
|
||||
created_at = '',
|
||||
|
|
@ -46,7 +46,7 @@ class TestDocumentResponse(unittest.TestCase):
|
|||
else:
|
||||
return DocumentResponse(
|
||||
id = '',
|
||||
agent_id = '',
|
||||
bank_id = '',
|
||||
original_text = '',
|
||||
content_hash = '',
|
||||
created_at = '',
|
||||
|
|
|
|||
|
|
@ -40,7 +40,9 @@ class TestEntityDetailResponse(unittest.TestCase):
|
|||
mention_count = 56,
|
||||
first_seen = '',
|
||||
last_seen = '',
|
||||
metadata = { },
|
||||
metadata = {
|
||||
'key' : null
|
||||
},
|
||||
observations = [
|
||||
hindsight_client_api.models.entity_observation_response.EntityObservationResponse(
|
||||
text = '',
|
||||
|
|
|
|||
|
|
@ -40,7 +40,9 @@ class TestEntityListItem(unittest.TestCase):
|
|||
mention_count = 56,
|
||||
first_seen = '',
|
||||
last_seen = '',
|
||||
metadata = { }
|
||||
metadata = {
|
||||
'key' : null
|
||||
}
|
||||
)
|
||||
else:
|
||||
return EntityListItem(
|
||||
|
|
|
|||
|
|
@ -35,13 +35,13 @@ class TestEntityListResponse(unittest.TestCase):
|
|||
model = EntityListResponse()
|
||||
if include_optional:
|
||||
return EntityListResponse(
|
||||
entities = [
|
||||
items = [
|
||||
{canonical_name=John, first_seen=2024-01-15T10:30:00Z, id=123e4567-e89b-12d3-a456-426614174000, last_seen=2024-02-01T14:00:00Z, mention_count=15}
|
||||
]
|
||||
)
|
||||
else:
|
||||
return EntityListResponse(
|
||||
entities = [
|
||||
items = [
|
||||
{canonical_name=John, first_seen=2024-01-15T10:30:00Z, id=123e4567-e89b-12d3-a456-426614174000, last_seen=2024-02-01T14:00:00Z, mention_count=15}
|
||||
],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -36,26 +36,38 @@ class TestGraphDataResponse(unittest.TestCase):
|
|||
if include_optional:
|
||||
return GraphDataResponse(
|
||||
nodes = [
|
||||
{ }
|
||||
{
|
||||
'key' : null
|
||||
}
|
||||
],
|
||||
edges = [
|
||||
{ }
|
||||
{
|
||||
'key' : null
|
||||
}
|
||||
],
|
||||
table_rows = [
|
||||
{ }
|
||||
{
|
||||
'key' : null
|
||||
}
|
||||
],
|
||||
total_units = 56
|
||||
)
|
||||
else:
|
||||
return GraphDataResponse(
|
||||
nodes = [
|
||||
{ }
|
||||
{
|
||||
'key' : null
|
||||
}
|
||||
],
|
||||
edges = [
|
||||
{ }
|
||||
{
|
||||
'key' : null
|
||||
}
|
||||
],
|
||||
table_rows = [
|
||||
{ }
|
||||
{
|
||||
'key' : null
|
||||
}
|
||||
],
|
||||
total_units = 56,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ class TestIncludeOptions(unittest.TestCase):
|
|||
if include_optional:
|
||||
return IncludeOptions(
|
||||
entities = hindsight_client_api.models.entity_include_options.EntityIncludeOptions(
|
||||
max_tokens = 56, ),
|
||||
chunks = hindsight_client_api.models.chunk_include_options.ChunkIncludeOptions(
|
||||
max_tokens = 56, )
|
||||
)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,9 @@ class TestListDocumentsResponse(unittest.TestCase):
|
|||
if include_optional:
|
||||
return ListDocumentsResponse(
|
||||
items = [
|
||||
{ }
|
||||
{
|
||||
'key' : null
|
||||
}
|
||||
],
|
||||
total = 56,
|
||||
limit = 56,
|
||||
|
|
@ -45,7 +47,9 @@ class TestListDocumentsResponse(unittest.TestCase):
|
|||
else:
|
||||
return ListDocumentsResponse(
|
||||
items = [
|
||||
{ }
|
||||
{
|
||||
'key' : null
|
||||
}
|
||||
],
|
||||
total = 56,
|
||||
limit = 56,
|
||||
|
|
|
|||
|
|
@ -36,7 +36,9 @@ class TestListMemoryUnitsResponse(unittest.TestCase):
|
|||
if include_optional:
|
||||
return ListMemoryUnitsResponse(
|
||||
items = [
|
||||
{ }
|
||||
{
|
||||
'key' : null
|
||||
}
|
||||
],
|
||||
total = 56,
|
||||
limit = 56,
|
||||
|
|
@ -45,7 +47,9 @@ class TestListMemoryUnitsResponse(unittest.TestCase):
|
|||
else:
|
||||
return ListMemoryUnitsResponse(
|
||||
items = [
|
||||
{ }
|
||||
{
|
||||
'key' : null
|
||||
}
|
||||
],
|
||||
total = 56,
|
||||
limit = 56,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
# coding: utf-8
|
||||
|
||||
"""
|
||||
Hindsight HTTP API
|
||||
|
||||
HTTP API for Hindsight
|
||||
|
||||
The version of the OpenAPI document: 1.0.0
|
||||
Generated by OpenAPI Generator (https://openapi-generator.tech)
|
||||
|
||||
Do not edit the class manually.
|
||||
""" # noqa: E501
|
||||
|
||||
|
||||
import unittest
|
||||
|
||||
from hindsight_client_api.api.monitoring_api import MonitoringApi
|
||||
|
||||
|
||||
class TestMonitoringApi(unittest.IsolatedAsyncioTestCase):
|
||||
"""MonitoringApi unit test stubs"""
|
||||
|
||||
async def asyncSetUp(self) -> None:
|
||||
self.api = MonitoringApi()
|
||||
|
||||
async def asyncTearDown(self) -> None:
|
||||
await self.api.api_client.close()
|
||||
|
||||
async def test_metrics_endpoint_metrics_get(self) -> None:
|
||||
"""Test case for metrics_endpoint_metrics_get
|
||||
|
||||
Prometheus metrics endpoint
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
@ -48,6 +48,8 @@ class TestRecallRequest(unittest.TestCase):
|
|||
],
|
||||
include = hindsight_client_api.models.include_options.IncludeOptions(
|
||||
entities = hindsight_client_api.models.entity_include_options.EntityIncludeOptions(
|
||||
max_tokens = 56, ),
|
||||
chunks = hindsight_client_api.models.chunk_include_options.ChunkIncludeOptions(
|
||||
max_tokens = 56, ), )
|
||||
)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -36,9 +36,11 @@ class TestRecallResponse(unittest.TestCase):
|
|||
if include_optional:
|
||||
return RecallResponse(
|
||||
results = [
|
||||
{context=work info, document_id=session_abc123, entities=[Alice, Google], id=123e4567-e89b-12d3-a456-426614174000, mentioned_at=2024-01-15T10:30:00Z, metadata={source=slack}, occurred_end=2024-01-15T10:30:00Z, occurred_start=2024-01-15T10:30:00Z, text=Alice works at Google on the AI team, type=world}
|
||||
{chunk_id=456e7890-e12b-34d5-a678-901234567890, context=work info, document_id=session_abc123, entities=[Alice, Google], id=123e4567-e89b-12d3-a456-426614174000, mentioned_at=2024-01-15T10:30:00Z, metadata={source=slack}, occurred_end=2024-01-15T10:30:00Z, occurred_start=2024-01-15T10:30:00Z, text=Alice works at Google on the AI team, type=world}
|
||||
],
|
||||
trace = { },
|
||||
trace = {
|
||||
'key' : null
|
||||
},
|
||||
entities = {
|
||||
'key' : hindsight_client_api.models.entity_state_response.EntityStateResponse(
|
||||
entity_id = '',
|
||||
|
|
@ -48,12 +50,19 @@ class TestRecallResponse(unittest.TestCase):
|
|||
text = '',
|
||||
mentioned_at = '', )
|
||||
], )
|
||||
},
|
||||
chunks = {
|
||||
'key' : hindsight_client_api.models.chunk_data.ChunkData(
|
||||
id = '',
|
||||
text = '',
|
||||
chunk_index = 56,
|
||||
truncated = True, )
|
||||
}
|
||||
)
|
||||
else:
|
||||
return RecallResponse(
|
||||
results = [
|
||||
{context=work info, document_id=session_abc123, entities=[Alice, Google], id=123e4567-e89b-12d3-a456-426614174000, mentioned_at=2024-01-15T10:30:00Z, metadata={source=slack}, occurred_end=2024-01-15T10:30:00Z, occurred_start=2024-01-15T10:30:00Z, text=Alice works at Google on the AI team, type=world}
|
||||
{chunk_id=456e7890-e12b-34d5-a678-901234567890, context=work info, document_id=session_abc123, entities=[Alice, Google], id=123e4567-e89b-12d3-a456-426614174000, mentioned_at=2024-01-15T10:30:00Z, metadata={source=slack}, occurred_end=2024-01-15T10:30:00Z, occurred_start=2024-01-15T10:30:00Z, text=Alice works at Google on the AI team, type=world}
|
||||
],
|
||||
)
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@ class TestRecallResult(unittest.TestCase):
|
|||
document_id = '',
|
||||
metadata = {
|
||||
'key' : ''
|
||||
}
|
||||
},
|
||||
chunk_id = ''
|
||||
)
|
||||
else:
|
||||
return RecallResult(
|
||||
|
|
|
|||
40
hindsight-clients/rust/Cargo.lock
generated
40
hindsight-clients/rust/Cargo.lock
generated
|
|
@ -86,9 +86,9 @@ checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.47"
|
||||
version = "1.2.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07"
|
||||
checksum = "c481bdbf0ed3b892f6f806287d72acd515b352a4ec27a208489b8c1bc839633a"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
|
|
@ -641,9 +641,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
|||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.82"
|
||||
version = "0.3.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65"
|
||||
checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"wasm-bindgen",
|
||||
|
|
@ -1081,9 +1081,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.13.0"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a"
|
||||
checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c"
|
||||
dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
|
@ -1572,9 +1572,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
|
|||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.41"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
|
||||
checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647"
|
||||
dependencies = [
|
||||
"pin-project-lite",
|
||||
"tracing-core",
|
||||
|
|
@ -1716,9 +1716,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.105"
|
||||
version = "0.2.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60"
|
||||
checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
|
|
@ -1729,9 +1729,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.55"
|
||||
version = "0.4.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0"
|
||||
checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
|
|
@ -1742,9 +1742,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.105"
|
||||
version = "0.2.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2"
|
||||
checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
|
@ -1752,9 +1752,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.105"
|
||||
version = "0.2.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc"
|
||||
checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
|
|
@ -1765,9 +1765,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.105"
|
||||
version = "0.2.106"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76"
|
||||
checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
|
@ -1787,9 +1787,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.82"
|
||||
version = "0.3.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1"
|
||||
checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"rustc_fingerprint":12740812217871447607,"outputs":{"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.91.1 (ed61e7d7e 2025-11-07)\nbinary: rustc\ncommit-hash: ed61e7d7e242494fb7057f2657300d9e77bb4fcb\ncommit-date: 2025-11-07\nhost: aarch64-apple-darwin\nrelease: 1.91.1\nLLVM version: 21.1.2\n","stderr":""},"7971740275564407648":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/nicoloboschi/.rustup/toolchains/stable-aarch64-apple-darwin\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"dit\"\ntarget_feature=\"dotprod\"\ntarget_feature=\"dpb\"\ntarget_feature=\"dpb2\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fhm\"\ntarget_feature=\"flagm\"\ntarget_feature=\"fp16\"\ntarget_feature=\"frintts\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"lor\"\ntarget_feature=\"lse\"\ntarget_feature=\"neon\"\ntarget_feature=\"paca\"\ntarget_feature=\"pacg\"\ntarget_feature=\"pan\"\ntarget_feature=\"pmuv3\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rcpc2\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sb\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_feature=\"ssbs\"\ntarget_feature=\"vh\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"apple\"\nunix\n","stderr":""}},"successes":{}}
|
||||
{"rustc_fingerprint":12740812217871447607,"outputs":{"7971740275564407648":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.dylib\nlib___.dylib\nlib___.a\nlib___.dylib\n/Users/nicoloboschi/.rustup/toolchains/stable-aarch64-apple-darwin\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"aarch64\"\ntarget_endian=\"little\"\ntarget_env=\"\"\ntarget_family=\"unix\"\ntarget_feature=\"aes\"\ntarget_feature=\"crc\"\ntarget_feature=\"dit\"\ntarget_feature=\"dotprod\"\ntarget_feature=\"dpb\"\ntarget_feature=\"dpb2\"\ntarget_feature=\"fcma\"\ntarget_feature=\"fhm\"\ntarget_feature=\"flagm\"\ntarget_feature=\"fp16\"\ntarget_feature=\"frintts\"\ntarget_feature=\"jsconv\"\ntarget_feature=\"lor\"\ntarget_feature=\"lse\"\ntarget_feature=\"neon\"\ntarget_feature=\"paca\"\ntarget_feature=\"pacg\"\ntarget_feature=\"pan\"\ntarget_feature=\"pmuv3\"\ntarget_feature=\"ras\"\ntarget_feature=\"rcpc\"\ntarget_feature=\"rcpc2\"\ntarget_feature=\"rdm\"\ntarget_feature=\"sb\"\ntarget_feature=\"sha2\"\ntarget_feature=\"sha3\"\ntarget_feature=\"ssbs\"\ntarget_feature=\"vh\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"macos\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"apple\"\nunix\n","stderr":""},"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.91.1 (ed61e7d7e 2025-11-07)\nbinary: rustc\ncommit-hash: ed61e7d7e242494fb7057f2657300d9e77bb4fcb\ncommit-date: 2025-11-07\nhost: aarch64-apple-darwin\nrelease: 1.91.1\nLLVM version: 21.1.2\n","stderr":""}},"successes":{}}
|
||||
|
|
@ -0,0 +1 @@
|
|||
32eef61671486063
|
||||
|
|
@ -1 +1 @@
|
|||
{"rustc":16243257175721966122,"features":"[]","declared_features":"[\"stream\", \"unstable\"]","target":15216351499943135959,"profile":5627820096486484124,"path":3294765106753457380,"deps":[[1074848931188612602,"atomic_waker",false,4889363751394578966],[1345404220202658316,"fnv",false,12262759022379011476],[2620434475832828286,"http",false,9979032511492736550],[6240934600354534560,"indexmap",false,14247778757879965049],[6355489020061627772,"bytes",false,15546652703430663087],[7013762810557009322,"futures_sink",false,6950808712564450941],[7620660491849607393,"futures_core",false,13556608982339264378],[7720834239451334583,"tokio",false,814226396053303386],[8606274917505247608,"tracing",false,16881066759846826124],[14180297684929992518,"tokio_util",false,17441115838095451012],[14767213526276824509,"slab",false,16871163405192104457]],"local":[{"CheckDepInfo":{"dep_info":"release/.fingerprint/h2-923e5387638d1bd9/dep-lib-h2","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}
|
||||
{"rustc":16243257175721966122,"features":"[]","declared_features":"[\"stream\", \"unstable\"]","target":15216351499943135959,"profile":5627820096486484124,"path":3294765106753457380,"deps":[[1074848931188612602,"atomic_waker",false,4889363751394578966],[1345404220202658316,"fnv",false,12262759022379011476],[2620434475832828286,"http",false,9979032511492736550],[6240934600354534560,"indexmap",false,14247778757879965049],[6355489020061627772,"bytes",false,15546652703430663087],[7013762810557009322,"futures_sink",false,6950808712564450941],[7620660491849607393,"futures_core",false,13556608982339264378],[7720834239451334583,"tokio",false,814226396053303386],[13455815276518097497,"tracing",false,6512599870775761065],[14180297684929992518,"tokio_util",false,17441115838095451012],[14767213526276824509,"slab",false,16871163405192104457]],"local":[{"CheckDepInfo":{"dep_info":"release/.fingerprint/h2-860e5416284abc9d/dep-lib-h2","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}
|
||||
|
|
@ -1 +0,0 @@
|
|||
f92220dae287ca6b
|
||||
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
49b8126e9760ef6c
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"rustc":16243257175721966122,"features":"[]","declared_features":"[]","target":6828276606420267087,"profile":2040997289075261528,"path":10763286916239946207,"deps":[[350039288653093011,"build_script_build",false,4231486804270383637],[503842845364652431,"chrono",false,11144948474405894614],[1046219396048762255,"progenitor_client",false,13036815077551201483],[2620434475832828286,"http",false,9979032511492736550],[5404511084185685755,"url",false,17079058419592311478],[5802782114936492624,"reqwest",false,16326245460864990945],[7720834239451334583,"tokio",false,814226396053303386],[8008191657135824715,"thiserror",false,1675330904495433212],[12832915883349295919,"serde_json",false,11663418101978700483],[13548984313718623784,"serde",false,17261882564294632758]],"local":[{"CheckDepInfo":{"dep_info":"release/.fingerprint/hindsight-client-26b0bc308cced3ae/dep-lib-hindsight_client","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}
|
||||
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
0ce0dc8ecfbb6afc
|
||||
|
|
@ -1 +0,0 @@
|
|||
{"rustc":16243257175721966122,"features":"[]","declared_features":"[]","target":6828276606420267087,"profile":2040997289075261528,"path":10763286916239946207,"deps":[[350039288653093011,"build_script_build",false,2443880033246489740],[503842845364652431,"chrono",false,11144948474405894614],[1046219396048762255,"progenitor_client",false,16451196449178175391],[2620434475832828286,"http",false,9979032511492736550],[5404511084185685755,"url",false,17079058419592311478],[5802782114936492624,"reqwest",false,1592833881977325371],[7720834239451334583,"tokio",false,814226396053303386],[8008191657135824715,"thiserror",false,1675330904495433212],[12832915883349295919,"serde_json",false,11663418101978700483],[13548984313718623784,"serde",false,17261882564294632758]],"local":[{"CheckDepInfo":{"dep_info":"release/.fingerprint/hindsight-client-4329cb0e29911e3c/dep-lib-hindsight_client","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}
|
||||
|
|
@ -0,0 +1 @@
|
|||
15024a40d442b93a
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"rustc":16243257175721966122,"features":"","declared_features":"","target":0,"profile":0,"path":0,"deps":[[350039288653093011,"build_script_build",false,7490509127350218047]],"local":[{"RerunIfChanged":{"output":"release/build/hindsight-client-45e816fa8febabac/output","paths":["/Users/nicoloboschi/dev/memory-poc/openapi.json"]}}],"rustflags":[],"config":0,"compile_kind":0}
|
||||
|
|
@ -1 +0,0 @@
|
|||
8cac5327f167ea21
|
||||
|
|
@ -1 +0,0 @@
|
|||
{"rustc":16243257175721966122,"features":"","declared_features":"","target":0,"profile":0,"path":0,"deps":[[350039288653093011,"build_script_build",false,14160702128762829566]],"local":[{"RerunIfChanged":{"output":"release/build/hindsight-client-9933d827d3e3d55a/output","paths":["/Users/nicoloboschi/dev/memory-poc/openapi.json"]}}],"rustflags":[],"config":0,"compile_kind":0}
|
||||
|
|
@ -0,0 +1 @@
|
|||
3fed876a5da2f367
|
||||
|
|
@ -1 +1 @@
|
|||
{"rustc":16243257175721966122,"features":"[]","declared_features":"[]","target":5408242616063297496,"profile":1369601567987815722,"path":13767053534773805487,"deps":[[7988640081342112296,"syn",false,1809862803220272063],[9423015880379144908,"prettyplease",false,5158570001680563136],[9738901266855342370,"progenitor",false,7200400063597451810],[12832915883349295919,"serde_json",false,7203318985267246464],[16847286912798951732,"openapiv3",false,15609397813544834071]],"local":[{"CheckDepInfo":{"dep_info":"release/.fingerprint/hindsight-client-e0ea9bb30ad7f8a5/dep-build-script-build-script-build","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}
|
||||
{"rustc":16243257175721966122,"features":"[]","declared_features":"[]","target":5408242616063297496,"profile":1369601567987815722,"path":13767053534773805487,"deps":[[7988640081342112296,"syn",false,1809862803220272063],[9423015880379144908,"prettyplease",false,5158570001680563136],[9738901266855342370,"progenitor",false,5156049257603408733],[12832915883349295919,"serde_json",false,7203318985267246464],[16847286912798951732,"openapiv3",false,15609397813544834071]],"local":[{"CheckDepInfo":{"dep_info":"release/.fingerprint/hindsight-client-b1a7aa8cd48fa221/dep-build-script-build-script-build","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}
|
||||
|
|
@ -1 +0,0 @@
|
|||
feba063b8feb84c4
|
||||
|
|
@ -0,0 +1 @@
|
|||
ea6ea6cd23c4b79b
|
||||
|
|
@ -1 +1 @@
|
|||
{"rustc":16243257175721966122,"features":"[\"client\", \"default\", \"http1\", \"http2\"]","declared_features":"[\"capi\", \"client\", \"default\", \"ffi\", \"full\", \"http1\", \"http2\", \"nightly\", \"server\", \"tracing\"]","target":9574292076208557625,"profile":5592815138508651293,"path":3727995574998783824,"deps":[[1074848931188612602,"atomic_waker",false,4889363751394578966],[1569313478171189446,"want",false,10044339025105331758],[1615478164327904835,"pin_utils",false,14683532508558912330],[1811549171721445101,"futures_channel",false,7366921400749317677],[1906322745568073236,"pin_project_lite",false,2890517474304306842],[2620434475832828286,"http",false,9979032511492736550],[3666196340704888985,"smallvec",false,2762008496713994936],[4133939468654419887,"h2",false,7767169915745739513],[6163892036024256188,"httparse",false,862024765873499899],[6355489020061627772,"bytes",false,15546652703430663087],[7620660491849607393,"futures_core",false,13556608982339264378],[7695812897323945497,"itoa",false,1828906794629363435],[7720834239451334583,"tokio",false,814226396053303386],[14084095096285906100,"http_body",false,6661408876623437285]],"local":[{"CheckDepInfo":{"dep_info":"release/.fingerprint/hyper-68b92baf42be0922/dep-lib-hyper","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}
|
||||
{"rustc":16243257175721966122,"features":"[\"client\", \"default\", \"http1\", \"http2\"]","declared_features":"[\"capi\", \"client\", \"default\", \"ffi\", \"full\", \"http1\", \"http2\", \"nightly\", \"server\", \"tracing\"]","target":9574292076208557625,"profile":5592815138508651293,"path":3727995574998783824,"deps":[[1074848931188612602,"atomic_waker",false,4889363751394578966],[1569313478171189446,"want",false,10044339025105331758],[1615478164327904835,"pin_utils",false,14683532508558912330],[1811549171721445101,"futures_channel",false,7366921400749317677],[1906322745568073236,"pin_project_lite",false,2890517474304306842],[2620434475832828286,"http",false,9979032511492736550],[3666196340704888985,"smallvec",false,2762008496713994936],[4133939468654419887,"h2",false,7160803058072874546],[6163892036024256188,"httparse",false,862024765873499899],[6355489020061627772,"bytes",false,15546652703430663087],[7620660491849607393,"futures_core",false,13556608982339264378],[7695812897323945497,"itoa",false,1828906794629363435],[7720834239451334583,"tokio",false,814226396053303386],[14084095096285906100,"http_body",false,6661408876623437285]],"local":[{"CheckDepInfo":{"dep_info":"release/.fingerprint/hyper-0c7c521e5ae1d609/dep-lib-hyper","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}
|
||||
|
|
@ -1 +0,0 @@
|
|||
b2c052550b4da367
|
||||
|
|
@ -0,0 +1 @@
|
|||
beb81a40d25690b8
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"rustc":16243257175721966122,"features":"[]","declared_features":"[\"alpn\", \"vendored\"]","target":11005878871305885301,"profile":2040997289075261528,"path":5681533078161436566,"deps":[[554721338292256162,"hyper_util",false,17873901682716089071],[784494742817713399,"tower_service",false,7356265403547447364],[4160778395972110362,"hyper",false,11220652654670016234],[6355489020061627772,"bytes",false,15546652703430663087],[7720834239451334583,"tokio",false,814226396053303386],[12186126227181294540,"tokio_native_tls",false,7383892010931007826],[16785601910559813697,"native_tls",false,18042093116010566256],[16900715236047033623,"http_body_util",false,7221940639537536546]],"local":[{"CheckDepInfo":{"dep_info":"release/.fingerprint/hyper-tls-34b77110d6698171/dep-lib-hyper_tls","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}
|
||||
|
|
@ -1 +0,0 @@
|
|||
5ae80d25b5aeb025
|
||||
|
|
@ -1 +0,0 @@
|
|||
{"rustc":16243257175721966122,"features":"[]","declared_features":"[\"alpn\", \"vendored\"]","target":11005878871305885301,"profile":2040997289075261528,"path":5681533078161436566,"deps":[[554721338292256162,"hyper_util",false,10324504803424377305],[784494742817713399,"tower_service",false,7356265403547447364],[4160778395972110362,"hyper",false,7467897318181879986],[6355489020061627772,"bytes",false,15546652703430663087],[7720834239451334583,"tokio",false,814226396053303386],[12186126227181294540,"tokio_native_tls",false,7383892010931007826],[16785601910559813697,"native_tls",false,18042093116010566256],[16900715236047033623,"http_body_util",false,7221940639537536546]],"local":[{"CheckDepInfo":{"dep_info":"release/.fingerprint/hyper-tls-59dc4b2da9834c15/dep-lib-hyper_tls","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}
|
||||
|
|
@ -1 +0,0 @@
|
|||
d999ecc96a02488f
|
||||
|
|
@ -0,0 +1 @@
|
|||
ef9a8c64e1da0cf8
|
||||
|
|
@ -1 +1 @@
|
|||
{"rustc":16243257175721966122,"features":"[\"client\", \"client-legacy\", \"client-proxy\", \"client-proxy-system\", \"default\", \"http1\", \"http2\", \"tokio\"]","declared_features":"[\"__internal_happy_eyeballs_tests\", \"client\", \"client-legacy\", \"client-proxy\", \"client-proxy-system\", \"default\", \"full\", \"http1\", \"http2\", \"server\", \"server-auto\", \"server-graceful\", \"service\", \"tokio\", \"tracing\"]","target":11100538814903412163,"profile":2040997289075261528,"path":3239747996768537754,"deps":[[95042085696191081,"ipnet",false,10405193789034471686],[784494742817713399,"tower_service",false,7356265403547447364],[985115344064483054,"system_configuration",false,4059817047406425165],[1811549171721445101,"futures_channel",false,7366921400749317677],[1906322745568073236,"pin_project_lite",false,2890517474304306842],[2620434475832828286,"http",false,9979032511492736550],[4160778395972110362,"hyper",false,7467897318181879986],[6355489020061627772,"bytes",false,15546652703430663087],[6803352382179706244,"percent_encoding",false,3530911331212444045],[7620660491849607393,"futures_core",false,13556608982339264378],[7720834239451334583,"tokio",false,814226396053303386],[8606274917505247608,"tracing",false,16881066759846826124],[10629569228670356391,"futures_util",false,13661049276535558845],[11499138078358568213,"libc",false,17790664046185964660],[11667313607130374549,"socket2",false,156739536956761713],[13077212702700853852,"base64",false,8599405015201889959],[14084095096285906100,"http_body",false,6661408876623437285]],"local":[{"CheckDepInfo":{"dep_info":"release/.fingerprint/hyper-util-2cb0a97ae1a39f9f/dep-lib-hyper_util","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}
|
||||
{"rustc":16243257175721966122,"features":"[\"client\", \"client-legacy\", \"client-proxy\", \"client-proxy-system\", \"default\", \"http1\", \"http2\", \"tokio\"]","declared_features":"[\"__internal_happy_eyeballs_tests\", \"client\", \"client-legacy\", \"client-proxy\", \"client-proxy-system\", \"default\", \"full\", \"http1\", \"http2\", \"server\", \"server-auto\", \"server-graceful\", \"service\", \"tokio\", \"tracing\"]","target":11100538814903412163,"profile":2040997289075261528,"path":3239747996768537754,"deps":[[95042085696191081,"ipnet",false,10405193789034471686],[784494742817713399,"tower_service",false,7356265403547447364],[985115344064483054,"system_configuration",false,4059817047406425165],[1811549171721445101,"futures_channel",false,7366921400749317677],[1906322745568073236,"pin_project_lite",false,2890517474304306842],[2620434475832828286,"http",false,9979032511492736550],[4160778395972110362,"hyper",false,11220652654670016234],[6355489020061627772,"bytes",false,15546652703430663087],[6803352382179706244,"percent_encoding",false,3530911331212444045],[7620660491849607393,"futures_core",false,13556608982339264378],[7720834239451334583,"tokio",false,814226396053303386],[10629569228670356391,"futures_util",false,13661049276535558845],[11499138078358568213,"libc",false,17790664046185964660],[11667313607130374549,"socket2",false,156739536956761713],[13077212702700853852,"base64",false,8599405015201889959],[13455815276518097497,"tracing",false,6512599870775761065],[14084095096285906100,"http_body",false,6661408876623437285]],"local":[{"CheckDepInfo":{"dep_info":"release/.fingerprint/hyper-util-78bd33f05bd2355b/dep-lib-hyper_util","checksum":false}}],"rustflags":[],"config":2069994364910194474,"compile_kind":0}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue