compair-core 0.4.0__tar.gz → 0.4.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of compair-core might be problematic. Click here for more details.
- {compair_core-0.4.0 → compair_core-0.4.1}/PKG-INFO +1 -1
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/compair/feedback.py +106 -39
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core.egg-info/PKG-INFO +1 -1
- {compair_core-0.4.0 → compair_core-0.4.1}/pyproject.toml +1 -1
- {compair_core-0.4.0 → compair_core-0.4.1}/LICENSE +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/README.md +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/__init__.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/api.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/compair/__init__.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/compair/celery_app.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/compair/default_groups.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/compair/embeddings.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/compair/logger.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/compair/main.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/compair/models.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/compair/schema.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/compair/tasks.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/compair/utils.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/compair_email/__init__.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/compair_email/email.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/compair_email/email_core.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/compair_email/templates.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/compair_email/templates_core.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/server/__init__.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/server/app.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/server/deps.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/server/local_model/__init__.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/server/local_model/app.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/server/local_model/ocr.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/server/providers/__init__.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/server/providers/console_mailer.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/server/providers/contracts.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/server/providers/local_storage.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/server/providers/noop_analytics.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/server/providers/noop_billing.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/server/providers/noop_ocr.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/server/routers/__init__.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/server/routers/capabilities.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core/server/settings.py +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core.egg-info/SOURCES.txt +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core.egg-info/dependency_links.txt +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core.egg-info/requires.txt +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/compair_core.egg-info/top_level.txt +0 -0
- {compair_core-0.4.0 → compair_core-0.4.1}/setup.cfg +0 -0
|
@@ -96,6 +96,33 @@ def _fallback_feedback(text: str, references: list[Any]) -> str:
|
|
|
96
96
|
return f"Consider aligning with these reference passages: {joined}"
|
|
97
97
|
|
|
98
98
|
|
|
99
|
+
|
|
100
|
+
def _local_reference_feedback(
|
|
101
|
+
reviewer: Reviewer,
|
|
102
|
+
references: list[Any],
|
|
103
|
+
user: User,
|
|
104
|
+
) -> str | None:
|
|
105
|
+
if not references:
|
|
106
|
+
return None
|
|
107
|
+
summaries: list[str] = []
|
|
108
|
+
for ref in references[:3]:
|
|
109
|
+
doc = getattr(ref, "document", None)
|
|
110
|
+
title = getattr(doc, "title", None) or "a related document"
|
|
111
|
+
snippet = getattr(ref, "content", "") or getattr(ref, "text", "")
|
|
112
|
+
snippet = snippet.replace("\n", " ").strip()
|
|
113
|
+
if not snippet:
|
|
114
|
+
continue
|
|
115
|
+
summaries.append(f'"{title}" — {snippet[:200]}')
|
|
116
|
+
if not summaries:
|
|
117
|
+
return None
|
|
118
|
+
instruction = reviewer.length_map.get(user.preferred_feedback_length, "1–2 short sentences")
|
|
119
|
+
if len(summaries) == 1:
|
|
120
|
+
body = summaries[0]
|
|
121
|
+
else:
|
|
122
|
+
body = "; ".join(summaries)
|
|
123
|
+
return f"[local-feedback] {instruction}: Consider the guidance from {body}"
|
|
124
|
+
|
|
125
|
+
|
|
99
126
|
def _openai_feedback(
|
|
100
127
|
reviewer: Reviewer,
|
|
101
128
|
doc: Document,
|
|
@@ -107,56 +134,92 @@ def _openai_feedback(
|
|
|
107
134
|
return None
|
|
108
135
|
instruction = reviewer.length_map.get(user.preferred_feedback_length, "1–2 short sentences")
|
|
109
136
|
ref_text = "\n\n".join(_reference_snippets(references, limit=3))
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
137
|
+
system_prompt = (
|
|
138
|
+
"You are Compair, an assistant that delivers concise, actionable feedback on a user's document. "
|
|
139
|
+
"Focus on clarity, cohesion, and usefulness."
|
|
140
|
+
)
|
|
141
|
+
user_prompt = (
|
|
142
|
+
f"Document:\n{text}\n\nHelpful reference excerpts:\n{ref_text or 'None provided'}\n\n"
|
|
143
|
+
f"Respond with {instruction} that highlights the most valuable revision to make next."
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
def _extract_response_text(response: Any) -> str | None:
|
|
147
|
+
if response is None:
|
|
148
|
+
return None
|
|
149
|
+
text_out = getattr(response, "output_text", None)
|
|
150
|
+
if isinstance(text_out, str) and text_out.strip():
|
|
151
|
+
return text_out.strip()
|
|
152
|
+
outputs = getattr(response, "output", None) or getattr(response, "outputs", None)
|
|
153
|
+
pieces: list[str] = []
|
|
154
|
+
if outputs:
|
|
155
|
+
for item in outputs:
|
|
156
|
+
content_field = None
|
|
157
|
+
if isinstance(item, dict):
|
|
158
|
+
content_field = item.get("content")
|
|
159
|
+
else:
|
|
160
|
+
content_field = getattr(item, "content", None)
|
|
161
|
+
if not content_field:
|
|
162
|
+
continue
|
|
163
|
+
for part in content_field:
|
|
164
|
+
if isinstance(part, dict):
|
|
165
|
+
val = part.get("text") or part.get("output_text")
|
|
166
|
+
if val:
|
|
167
|
+
pieces.append(str(val))
|
|
168
|
+
elif part:
|
|
169
|
+
pieces.append(str(part))
|
|
170
|
+
if pieces:
|
|
171
|
+
merged = "\n".join(pieces).strip()
|
|
172
|
+
return merged or None
|
|
173
|
+
return None
|
|
126
174
|
|
|
127
175
|
try:
|
|
128
|
-
|
|
129
|
-
|
|
176
|
+
client = reviewer._openai_client
|
|
177
|
+
if client is None and hasattr(openai, "OpenAI"):
|
|
178
|
+
api_key = os.getenv("COMPAIR_OPENAI_API_KEY") or None
|
|
179
|
+
try: # pragma: no cover - optional dependency differences
|
|
180
|
+
client = openai.OpenAI(api_key=api_key) if api_key else openai.OpenAI()
|
|
181
|
+
except TypeError:
|
|
182
|
+
client = openai.OpenAI()
|
|
183
|
+
reviewer._openai_client = client
|
|
184
|
+
|
|
185
|
+
content: str | None = None
|
|
186
|
+
if client is not None and hasattr(client, "responses"):
|
|
187
|
+
response = client.responses.create(
|
|
130
188
|
model=reviewer.openai_model,
|
|
131
|
-
|
|
189
|
+
instructions=system_prompt,
|
|
190
|
+
input=user_prompt,
|
|
132
191
|
max_output_tokens=256,
|
|
192
|
+
store=False,
|
|
133
193
|
)
|
|
134
|
-
content =
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
194
|
+
content = _extract_response_text(response)
|
|
195
|
+
elif client is not None and hasattr(client, "chat") and hasattr(client.chat, "completions"):
|
|
196
|
+
response = client.chat.completions.create(
|
|
197
|
+
model=reviewer.openai_model,
|
|
198
|
+
messages=[{"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}],
|
|
199
|
+
temperature=0.3,
|
|
200
|
+
max_tokens=256,
|
|
201
|
+
)
|
|
202
|
+
choices = getattr(response, "choices", None) or []
|
|
203
|
+
if choices:
|
|
204
|
+
message = getattr(choices[0], "message", None)
|
|
205
|
+
if message is not None:
|
|
206
|
+
content = getattr(message, "content", None)
|
|
207
|
+
if not content:
|
|
208
|
+
content = getattr(choices[0], "text", None)
|
|
209
|
+
if isinstance(content, str):
|
|
210
|
+
content = content.strip()
|
|
141
211
|
elif hasattr(openai, "ChatCompletion"):
|
|
142
212
|
chat_response = openai.ChatCompletion.create( # type: ignore[attr-defined]
|
|
143
213
|
model=reviewer.openai_model,
|
|
144
|
-
messages=
|
|
214
|
+
messages=[{"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}],
|
|
145
215
|
temperature=0.3,
|
|
146
216
|
max_tokens=256,
|
|
147
217
|
)
|
|
148
|
-
content = (
|
|
149
|
-
|
|
150
|
-
)
|
|
151
|
-
else:
|
|
152
|
-
content = None
|
|
218
|
+
content = chat_response["choices"][0]["message"]["content"].strip() # type: ignore[index, assignment]
|
|
219
|
+
if content:
|
|
220
|
+
return content.strip()
|
|
153
221
|
except Exception as exc: # pragma: no cover - network/API failure
|
|
154
222
|
log_event("openai_feedback_failed", error=str(exc))
|
|
155
|
-
content = None
|
|
156
|
-
if content:
|
|
157
|
-
content = content.strip()
|
|
158
|
-
if content:
|
|
159
|
-
return content
|
|
160
223
|
return None
|
|
161
224
|
|
|
162
225
|
|
|
@@ -238,9 +301,13 @@ def get_feedback(
|
|
|
238
301
|
if feedback:
|
|
239
302
|
return feedback
|
|
240
303
|
|
|
241
|
-
if reviewer.provider == "local"
|
|
242
|
-
feedback =
|
|
304
|
+
if reviewer.provider == "local":
|
|
305
|
+
feedback = _local_reference_feedback(reviewer, references, user)
|
|
243
306
|
if feedback:
|
|
244
307
|
return feedback
|
|
308
|
+
if getattr(reviewer, "endpoint", None):
|
|
309
|
+
feedback = _local_feedback(reviewer, text, references, user)
|
|
310
|
+
if feedback:
|
|
311
|
+
return feedback
|
|
245
312
|
|
|
246
313
|
return _fallback_feedback(text, references)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|