Views
No views yet
pplx-embed-v1 and pplx-embed-context-v1 are state-of-the-art text embedding models optimized for real-world, web-scale retrieval tasks.pplx-embed-v1 for independent text embedding (queries, documents, semantic search)pplx-embed-context-v1 for document chunks in RAG systems where surrounding context matters[!IMPORTANT]pplx-embed-v1andpplx-embed-context-v1natively produce unnormalized int8-quantized embeddings. Ensure that you compare them via cosine similarity.

| Model | Dimensions | Context | MRL | Quantization | Instruction | Pooling |
|---|---|---|---|---|---|---|
pplx-embed-v1-0.6B | 1024 | 32K | Yes | INT8/BINARY | No | Mean |
pplx-embed-v1-4B | 2560 | 32K | Yes | INT8/BINARY | No | Mean |
pplx-embed-context-v1-0.6B | 1024 | 32K | Yes | INT8/BINARY | No | Mean |
pplx-embed-context-v1-4B | 2560 | 32K | Yes | INT8/BINARY | No | Mean |
1curl -X POST https://api.perplexity.ai/v1/contextualizedembeddings \
2 -H "Authorization: Bearer YOUR_API_KEY" \
3 -H "Content-Type: application/json" \
4 -d '{
5 "input": [
6 [
7 "Curiosity begins in childhood with endless questions about the world.",
8 "As we grow, curiosity drives us to explore new ideas and challenge assumptions.",
9 "Scientific breakthroughs often start with a simple curious question."
10 ],
11 [
12 "The curiosity rover explores Mars, searching for signs of ancient life.",
13 "Each discovery on Mars sparks new questions about our place in the universe."
14 ]
15 ],
16 "model": "pplx-embed-context-v1-0.6b"
17 }'1from transformers import AutoModel
2
3model_ctx = AutoModel.from_pretrained(
4 "perplexity-ai/pplx-embed-context-v1-0.6B",
5 trust_remote_code=True
6)
7
8doc_chunks = [
9 [
10 "Curiosity begins in childhood with endless questions about the world.",
11 "As we grow, curiosity drives us to explore new ideas.",
12 "Scientific breakthroughs often start with a curious question."
13 ],
14 [
15 "The curiosity rover explores Mars searching for ancient life.",
16 "Each discovery on Mars sparks new questions about the universe."
17 ]
18]
19# Returns list of numpy arrays (one per document)
20# embeddings[0].shape = (3, 1024), embeddings[1].shape = (2, 1024)
21embeddings = model_ctx.encode(doc_chunks)1
2import onnxruntime as ort
3from transformers import AutoTokenizer
4import numpy as np
5import torch
6
7def quantize_int8_tanh(x):
8 normalized = torch.tanh(x)
9 rounded = torch.round(normalized * 127)
10 clamped = torch.clamp(rounded, -128, 127)
11 return clamped
12
13def quantize_binary(x):
14 return torch.where(x >= 0, 1.0, -1.0)
15
16def mean_pooling(
17 token_embeddings: torch.Tensor, attention_mask: torch.Tensor
18) -> torch.Tensor:
19 """Apply mean pooling to token embeddings."""
20 input_mask_expanded = (
21 attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float()
22 )
23 return torch.sum(token_embeddings * input_mask_expanded, 1) / torch.clamp(
24 input_mask_expanded.sum(1), min=1e-9
25 )
26
27def extract_chunks_from_concatenated(
28 input_ids: torch.Tensor,
29 token_embeddings: torch.Tensor,
30 attention_mask: torch.Tensor,
31 tokenizer,
32) -> list[list[torch.Tensor]]:
33 """
34 Extract individual chunk embeddings from concatenated sequence using late chunking.
35
36 This method splits concatenated sequences like "[chunk1][SEP][chunk2][SEP]..."
37 back into individual chunk embeddings by finding SEP token positions.
38
39 Args:
40 input_ids: Token IDs (batch_size, seq_len)
41 token_embeddings: Token embeddings (batch_size, seq_len, hidden_dim)
42 attention_mask: Attention mask (batch_size, seq_len)
43
44 Returns:
45 list[list[torch.Tensor]]: List of documents, each containing list of chunk embeddings
46
47 Note:
48 The sep_token_id is retrieved tokenizer.sep_token_id.
49 Common values: pplx-embed-1=151643, BERT=102, varies by tokenizer.
50 """
51 sep_token_id = tokenizer.sep_token_id
52 batch_size = input_ids.shape[0]
53
54 all_doc_chunks = []
55
56 for batch_idx in range(batch_size):
57 # non-pad sep tokens
58 valid_positions = attention_mask[batch_idx].bool()
59 sep_positions = (
60 (input_ids[batch_idx] == sep_token_id) & valid_positions
61 ).nonzero(as_tuple=True)[0]
62
63 chunk_embeddings = []
64 start_pos = 0
65
66 for sep_pos in sep_positions:
67 chunk_tokens = token_embeddings[batch_idx, start_pos:sep_pos]
68 chunk_mask = attention_mask[batch_idx, start_pos:sep_pos]
69
70 chunk_emb = mean_pooling(
71 chunk_tokens.unsqueeze(0), chunk_mask.unsqueeze(0)
72 ).squeeze(0)
73
74 chunk_embeddings.append(chunk_emb)
75
76 start_pos = sep_pos + 1
77
78 # Handle the last chunk (after the last SEP token)
79 last_valid_pos = attention_mask[batch_idx].sum().item()
80
81 chunk_tokens = token_embeddings[batch_idx, start_pos:last_valid_pos]
82 chunk_mask = attention_mask[batch_idx, start_pos:last_valid_pos]
83
84 if chunk_mask.sum() > 0:
85 chunk_emb = mean_pooling(
86 chunk_tokens.unsqueeze(0), chunk_mask.unsqueeze(0)
87 ).squeeze(0)
88 else:
89 # Empty chunk - create zero embedding
90 chunk_emb = torch.zeros(
91 token_embeddings.shape[-1],
92 device=token_embeddings.device,
93 dtype=token_embeddings.dtype,
94 )
95
96 chunk_embeddings.append(chunk_emb)
97
98 all_doc_chunks.append(chunk_embeddings)
99
100 return all_doc_chunks
101
102
103hf_path=("perplexity-ai/pplx-embed-context-v1-0.6b")
104onnx_path=("onnx/model.onnx")
105
106tokenizer = AutoTokenizer.from_pretrained(hf_path, trust_remote_code=True)
107session = ort.InferenceSession(onnx_path)
108
109texts = [
110 [
111 "Curiosity begins in childhood with endless questions about the world.",
112 "As we grow, curiosity drives us to explore new ideas.",
113 "Scientific breakthroughs often start with a curious question."
114 ],
115 [
116 "The curiosity rover explores Mars searching for ancient life.",
117 "Each discovery on Mars sparks new questions about the universe."
118 ]
119]
120doc_strings = [
121 tokenizer.sep_token.join(chunks) for chunks in texts
122]
123
124tokenized = tokenizer(
125 doc_strings,
126 padding=True,
127 truncation=True,
128 return_tensors="np",
129)
130onnx_inputs = {
131 "input_ids": tokenized["input_ids"].astype(np.int64),
132 "attention_mask": tokenized["attention_mask"].astype(np.int64),
133}
134
135# Run inference
136onnx_outputs = session.run([out.name for out in session.get_outputs()], onnx_inputs)
137# onnx_outputs is a list with one element: [last_hidden_state]
138last_hidden_state = onnx_outputs[0]
139
140batch_chunk_embeddings = extract_chunks_from_concatenated(
141 input_ids=torch.tensor(onnx_inputs["input_ids"]),
142 token_embeddings=torch.tensor(last_hidden_state),
143 attention_mask=torch.tensor(onnx_inputs["attention_mask"]),
144 tokenizer=tokenizer,
145)
146
147batch_chunk_embeddings = [
148 torch.stack([chunk for chunk in doc_chunks], dim=0)
149 for doc_chunks in batch_chunk_embeddings
150]
151
152int8_embeddings = [quantize_int8_tanh(x) for x in batch_chunk_embeddings]
153binary_embeddings = [quantize_binary(x) for x in batch_chunk_embeddings]
154
155bits = [np.where(doc.numpy() >= 0, True, False) for doc in binary_embeddings]
156packed_embeddings = [np.packbits(b, axis=-1) for b in bits]
157