Fade0510's picture
Add Scoring Rubric
18ddfa2
Raw
History Blame Contribute Delete
7.02 kB
import pandas as pd
import json
from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate
from src.agents.CallState import CallState
class IntakeAgent:
def __init__(self):
self.llm = ChatOpenAI(model="gpt-4o", temperature=0)
def __call__(self, state: CallState) -> CallState:
"""Validates input formats, cleans up profanity, extracts meta."""
file_path = state["file_path"]
file_type = state["file_type"]
state["metadata"] = {"file_type": file_type, "file_path": file_path}
if file_type == "csv":
try:
df = pd.read_csv(file_path)
normalized = {str(c).strip().lower(): c for c in df.columns}
required = {"id", "transcript"}
missing = sorted(required - set(normalized.keys()))
if missing:
state["metadata"]["intake_error"] = (
f"CSV is missing required headers: {', '.join(missing)}"
)
state["content"] = ""
state["clean_content"] = ""
return state
id_col = normalized["id"]
transcript_col = normalized["transcript"]
transcripts = df[transcript_col].fillna("").astype(str).tolist()
state["content"] = " ".join(transcripts).strip()
state["metadata"]["row_count"] = int(len(df))
state["metadata"]["ids_preview"] = (
df[id_col].fillna("").astype(str).head(20).tolist()
)
except Exception as e:
state["metadata"]["intake_error"] = str(e)
state["content"] = ""
state["clean_content"] = ""
return state
if file_type == "json":
try:
with open(file_path, "r", encoding="utf-8") as f:
raw = f.read()
try:
payload = json.loads(raw)
except json.JSONDecodeError:
# Common failure mode: users paste multi-line transcripts with raw control characters
# (e.g., literal newlines) inside JSON strings. JSON requires these to be escaped.
def _sanitize_control_chars_in_strings(text: str) -> str:
out: list[str] = []
in_string = False
escape = False
for ch in text:
if not in_string:
out.append(ch)
if ch == '"':
in_string = True
continue
# Inside string
if escape:
out.append(ch)
escape = False
continue
if ch == "\\":
out.append(ch)
escape = True
continue
if ch == '"':
out.append(ch)
in_string = False
continue
code = ord(ch)
if ch == "\n":
out.append("\\n")
elif ch == "\r":
out.append("\\r")
elif ch == "\t":
out.append("\\t")
elif code < 0x20:
out.append(f"\\u{code:04x}")
else:
out.append(ch)
return "".join(out)
payload = json.loads(_sanitize_control_chars_in_strings(raw))
transcripts: list[str] = []
ids_preview: list[str] = []
def add_item(item: object) -> None:
if not isinstance(item, dict):
return
transcript = item.get("transcript")
if isinstance(transcript, str) and transcript.strip():
transcripts.append(transcript.strip())
item_id = item.get("id")
if item_id is not None and len(ids_preview) < 20:
ids_preview.append(str(item_id))
if isinstance(payload, list):
for item in payload:
add_item(item)
elif isinstance(payload, dict):
if isinstance(payload.get("transcript"), str):
add_item(payload)
elif isinstance(payload.get("transcripts"), list):
for item in payload["transcripts"]:
add_item(item)
elif isinstance(payload.get("calls"), list):
for item in payload["calls"]:
add_item(item)
else:
state["metadata"]["intake_error"] = (
"JSON must be either a list of {id, transcript} objects, "
"a single {id, transcript} object, or a dict with 'transcripts'/'calls' arrays."
)
state["content"] = ""
state["clean_content"] = ""
return state
else:
state["metadata"]["intake_error"] = "Unsupported JSON root type."
state["content"] = ""
state["clean_content"] = ""
return state
if not transcripts:
state["metadata"]["intake_error"] = "JSON contained no non-empty 'transcript' fields."
state["content"] = ""
state["clean_content"] = ""
return state
state["content"] = " ".join(transcripts).strip()
state["metadata"]["row_count"] = int(len(transcripts))
if ids_preview:
state["metadata"]["ids_preview"] = ids_preview
except Exception as e:
state["metadata"]["intake_error"] = str(e)
state["content"] = ""
state["clean_content"] = ""
return state
if state.get("content"):
prompt = PromptTemplate.from_template(
"Clean the following text of any profanity and fix basic grammatical errors. Return only the clean text:\n{text}"
)
chain = prompt | self.llm
clean_text = chain.invoke({"text": state["content"]}).content
state["clean_content"] = clean_text
return state