gemcode 0.3.98__py3-none-any.whl → 0.3.99__py3-none-any.whl
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.
- gemcode/session_summariser.py +53 -13
- {gemcode-0.3.98.dist-info → gemcode-0.3.99.dist-info}/METADATA +1 -1
- {gemcode-0.3.98.dist-info → gemcode-0.3.99.dist-info}/RECORD +7 -7
- {gemcode-0.3.98.dist-info → gemcode-0.3.99.dist-info}/WHEEL +0 -0
- {gemcode-0.3.98.dist-info → gemcode-0.3.99.dist-info}/entry_points.txt +0 -0
- {gemcode-0.3.98.dist-info → gemcode-0.3.99.dist-info}/licenses/LICENSE +0 -0
- {gemcode-0.3.98.dist-info → gemcode-0.3.99.dist-info}/top_level.txt +0 -0
gemcode/session_summariser.py
CHANGED
|
@@ -100,16 +100,51 @@ def _build_prompt(transcript_lines: list[str], *, focus: str = "") -> str:
|
|
|
100
100
|
)
|
|
101
101
|
|
|
102
102
|
|
|
103
|
-
def
|
|
103
|
+
def _extract_json_object(text: str) -> dict[str, Any] | None:
|
|
104
|
+
"""
|
|
105
|
+
Best-effort parse:
|
|
106
|
+
- strict JSON object
|
|
107
|
+
- JSON object embedded in extra text (common model behavior)
|
|
108
|
+
"""
|
|
109
|
+
raw = (text or "").strip()
|
|
110
|
+
if not raw:
|
|
111
|
+
return None
|
|
112
|
+
try:
|
|
113
|
+
obj = json.loads(raw)
|
|
114
|
+
return obj if isinstance(obj, dict) else None
|
|
115
|
+
except Exception:
|
|
116
|
+
pass
|
|
117
|
+
|
|
118
|
+
# Try to recover embedded JSON: find the largest {...} span.
|
|
119
|
+
start = raw.find("{")
|
|
120
|
+
end = raw.rfind("}")
|
|
121
|
+
if start == -1 or end == -1 or end <= start:
|
|
122
|
+
return None
|
|
123
|
+
candidate = raw[start : end + 1].strip()
|
|
124
|
+
try:
|
|
125
|
+
obj = json.loads(candidate)
|
|
126
|
+
return obj if isinstance(obj, dict) else None
|
|
127
|
+
except Exception:
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _call_summary_model(*, model: str, prompt: str) -> tuple[dict[str, Any] | None, str]:
|
|
104
132
|
api_key = os.environ.get("GOOGLE_API_KEY")
|
|
105
133
|
if not api_key:
|
|
106
134
|
raise RuntimeError("GOOGLE_API_KEY not set")
|
|
107
135
|
|
|
108
136
|
client = Client(api_key=api_key)
|
|
137
|
+
cfg = types.GenerateContentConfig(temperature=0.2)
|
|
138
|
+
# Prefer structured JSON output when supported by the SDK/model.
|
|
139
|
+
try:
|
|
140
|
+
setattr(cfg, "response_mime_type", "application/json")
|
|
141
|
+
except Exception:
|
|
142
|
+
pass
|
|
143
|
+
|
|
109
144
|
resp = client.models.generate_content(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
145
|
+
model=model,
|
|
146
|
+
contents=[types.Content(role="user", parts=[types.Part(text=prompt)])],
|
|
147
|
+
config=cfg,
|
|
113
148
|
)
|
|
114
149
|
|
|
115
150
|
out_parts: list[str] = []
|
|
@@ -127,14 +162,7 @@ def _call_summary_model(*, model: str, prompt: str) -> dict[str, Any]:
|
|
|
127
162
|
text = "".join(out_parts).strip()
|
|
128
163
|
if not text:
|
|
129
164
|
raise RuntimeError("session summariser returned empty text")
|
|
130
|
-
|
|
131
|
-
try:
|
|
132
|
-
data = json.loads(text)
|
|
133
|
-
except Exception as e:
|
|
134
|
-
raise RuntimeError(f"session summariser returned invalid JSON: {e}") from e
|
|
135
|
-
if not isinstance(data, dict):
|
|
136
|
-
raise RuntimeError("session summariser returned non-object JSON")
|
|
137
|
-
return data
|
|
165
|
+
return _extract_json_object(text), text
|
|
138
166
|
|
|
139
167
|
|
|
140
168
|
def summarise_session(
|
|
@@ -149,7 +177,18 @@ def summarise_session(
|
|
|
149
177
|
return {"error": "session transcript is empty", "session_id": session_id}
|
|
150
178
|
|
|
151
179
|
prompt = _build_prompt(transcript_lines, focus=focus)
|
|
152
|
-
data = _call_summary_model(model=model, prompt=prompt)
|
|
180
|
+
data, raw_text = _call_summary_model(model=model, prompt=prompt)
|
|
181
|
+
if data is None:
|
|
182
|
+
# Graceful fallback: save raw text as summary_markdown so /summarise never hard-fails
|
|
183
|
+
# due to JSON formatting drift.
|
|
184
|
+
data = {
|
|
185
|
+
"title": f"Session {session_id[:8]}",
|
|
186
|
+
"summary_markdown": raw_text[:20_000],
|
|
187
|
+
"memory_facts": [],
|
|
188
|
+
"user_facts": [],
|
|
189
|
+
"notes_markdown": "",
|
|
190
|
+
"open_items": [],
|
|
191
|
+
}
|
|
153
192
|
|
|
154
193
|
title = str(data.get("title") or f"Session {session_id[:8]}").strip()[:120]
|
|
155
194
|
summary_markdown = str(data.get("summary_markdown") or "").strip()
|
|
@@ -219,6 +258,7 @@ def summarise_session(
|
|
|
219
258
|
"summary_path": str(out_path),
|
|
220
259
|
"title": title,
|
|
221
260
|
"summary_markdown": summary_markdown,
|
|
261
|
+
"used_json": bool(data is not None and _extract_json_object(raw_text) is not None),
|
|
222
262
|
"memory_facts_saved": saved_memory,
|
|
223
263
|
"user_facts_saved": saved_user,
|
|
224
264
|
"notes_status": note_status,
|
|
@@ -47,7 +47,7 @@ gemcode/review_agent.py,sha256=4t7_5-aE60b4-EheJ_eSB_H2eQYf9GppKoui6jw0TME,5264
|
|
|
47
47
|
gemcode/rules.py,sha256=Itg02VpifOo6jqGj5xwna_ahaPPb0OVtaeR2cNI0pLE,3018
|
|
48
48
|
gemcode/session_runtime.py,sha256=MGOWWUz4ZUnWkuaYkc5EZ_uYYIvLjJc1N35X2GUX79k,23489
|
|
49
49
|
gemcode/session_store.py,sha256=POUT_QQf715c74jbXj0s5vCd4dlAgJz_CLsIWuEUoO0,6051
|
|
50
|
-
gemcode/session_summariser.py,sha256=
|
|
50
|
+
gemcode/session_summariser.py,sha256=ixRJVGbgWGWP-tT8hGLEqBlZ3uMXJlwQdbFsYfwF4hk,8370
|
|
51
51
|
gemcode/skills.py,sha256=nnrzYUCiuEkU_i57p_jJpPHRfw1t2t2EA3pJHNqvpzw,12554
|
|
52
52
|
gemcode/slash_commands.py,sha256=bcD-S_H7p7AlTli6g2dLPPG46HejPje0Imb3ScDTCaQ,798
|
|
53
53
|
gemcode/thinking.py,sha256=-1TVkOMG-7CSQN0Mc18EqINkUxWOMBgeTlF7CX9zYL4,4641
|
|
@@ -107,9 +107,9 @@ gemcode/web/__init__.py,sha256=EysmUAWs6g-lmMk4VFljKfaHVrEgb_FiIzwQmBdORJc,40
|
|
|
107
107
|
gemcode/web/sse_adapter.py,sha256=fXhKxn_bdJJUGqlmvkxLNSYL-ZiIZDaLHtQCF_BheRc,7108
|
|
108
108
|
gemcode/web/terminal_repl.py,sha256=fQt895g0qcr6VBhXfv_5b_bsC5zHT5-MO0ysBdgi2Fg,3886
|
|
109
109
|
gemcode/web/web_sse_compat.py,sha256=9A2s-GI7El7AotJqhO263FrLwppCXXkdydZ5EiOQbao,504
|
|
110
|
-
gemcode-0.3.
|
|
111
|
-
gemcode-0.3.
|
|
112
|
-
gemcode-0.3.
|
|
113
|
-
gemcode-0.3.
|
|
114
|
-
gemcode-0.3.
|
|
115
|
-
gemcode-0.3.
|
|
110
|
+
gemcode-0.3.99.dist-info/licenses/LICENSE,sha256=TD4524qn-W8Z07GTDnag-9jJPFutFZNB0a1WbMHPC54,8388
|
|
111
|
+
gemcode-0.3.99.dist-info/METADATA,sha256=xCMSBtFzQrLEDtt9BawkHs1NfdmLTQbGf0lcJ-RHsWg,17084
|
|
112
|
+
gemcode-0.3.99.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
113
|
+
gemcode-0.3.99.dist-info/entry_points.txt,sha256=cZdLTLDiHbks7OSUCuxCh66dCWeQdpLR8BozoqfEjV4,45
|
|
114
|
+
gemcode-0.3.99.dist-info/top_level.txt,sha256=UYrjULLBY2bcgK6KI6flomJWmsbDXu7n0rvW2SWFrbo,8
|
|
115
|
+
gemcode-0.3.99.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|