Skip to content

Dev#10

Merged
HardMax71 merged 2 commits intomainfrom
dev
Dec 6, 2025
Merged

Dev#10
HardMax71 merged 2 commits intomainfrom
dev

Conversation

@HardMax71
Copy link
Owner

@HardMax71 HardMax71 commented Dec 6, 2025

Summary by CodeRabbit

  • New Features

    • Enhanced resume processing with expanded metadata tracking (page counts, vector/graph storage status, error tracking)
    • Improved resume data model with additional fields for backward compatibility
  • Improvements

    • Auto-generated type-safe API client integration between frontend and backend
    • Enhanced error handling and validation across resume processing and search features
    • Better structured API responses for education, language, and location data
    • Improved resume display rendering with comprehensive type safety

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 6, 2025

Walkthrough

This PR migrates the backend from Django REST Framework serializers to Pydantic models, adds backward-compatibility fields to domain models, and replaces the frontend's manual API client with an auto-generated OpenAPI TypeScript client. The changes centralize API contract management and modernize the codebase's type safety.

Changes

Cohort / File(s) Summary
Backend Domain Models – Pydantic Migration
backend/core/domain/processing.py
Converts ProcessingMetadata and ProcessingResult from dataclasses to Pydantic BaseModel; adds fields (page_count, graph_stored, vector_stored, vector_count, graph_error, vector_error, review_generated, review_error); introduces new ResumeResponse model with status enum and metadata.
Backend Domain Models – Legacy Compatibility
backend/core/domain/resume.py
Adds optional legacy fields to Contact (linkedin, github, website), Project (description), EducationItem (year, gpa), LanguageProficiency (name, level), Award (title, issuer, date), and Resume (name, email, phone, location, linkedin, github, website, resume_lang) for backward compatibility.
Backend Serializer Removal
backend/processor/serializers.py
Removes JobCreateSerializer, JobUpdateSerializer, JobSerializer, ResumeResponseSerializer, and ResumeListResponseSerializer; removes datetime import.
Backend API Layer – Pydantic Models
backend/processor/views.py
Introduces ResumeListResponse Pydantic model; replaces OpenAPI response type references from removed serializers to new Pydantic models (ResumeResponse, ResumeListResponse); updates FileConfigView signature.
Backend Processing Worker
backend/processor/workers/processing.py
Replaces manual result dict construction with Pydantic model_dump(); removes asdict import.
Backend Search Serializers
backend/search/serializers.py
Adds EducationEntrySerializer, LanguageEntrySerializer, LocationEntrySerializer; updates SearchResultSerializer fields to use nested serializers instead of DictField/ListField.
Frontend OpenAPI Code Generation Setup
docker-compose.yaml, frontend/openapi-ts.config.ts, frontend/package.json
Adds frontend startup command to wait for backend schema and generate API client; creates openapi-ts config; adds generate:api script and @hey-api dependencies.
Frontend API Client Layer
frontend/src/api/client.ts
New module configuring and re-exporting auto-generated API client from backend OpenAPI schema.
Frontend – Manual API Client Removal
frontend/src/lib/api.ts
Removes entire file containing manual API helpers (apiGet, apiPost, apiUpload, fetchWithTimeout), error classes (APIError, NetworkError), and domain type definitions (ResumeStatus, ResumeResponse, SearchFilters, SearchResult, etc.).
Frontend – Import Path Migration (Simple)
frontend/src/components/ResultCard.tsx, frontend/src/components/filters/LanguagesFilter.tsx, frontend/src/components/filters/LocationsFilter.tsx, frontend/src/pages/Health.tsx, frontend/src/pages/PrivacyPolicy.tsx
Update import paths from ../lib/api to ../api/client; no behavioral changes.
Frontend – Import Path & Type Migration
frontend/src/components/SearchFilters.tsx, frontend/src/components/filters/ActiveFiltersBar.tsx, frontend/src/hooks/useResumeSearch.ts, frontend/src/pages/Search.tsx
Update imports and replace SearchFilters with SearchFiltersSchema; update hook parameter types (SemanticSearchParams→VectorSearchQuerySchema, etc.).
Frontend – Component Type Updates
frontend/src/components/filters/EducationFilter.tsx
Adds StatusesEnum type casting for incoming statuses array; uses for membership checks and rendering.
Frontend – Hook Updates
frontend/src/hooks/useResumeUpload.ts
Removes type import of UploadResult from resumeService; keeps runtime import intact.
Frontend – Service Layer Migration
frontend/src/services/fileConfigService.ts, frontend/src/services/healthService.ts, frontend/src/services/ragService.ts, frontend/src/services/resumeService.ts
Migrate from manual apiGet/apiPost to typed v1** client functions; add error handling; replace local interfaces with imports from api/client; update function signatures (e.g., UploadResult→ResumeResponse, SemanticSearchParams→VectorSearchQuerySchema).
Frontend – Error Handling Refactor
frontend/src/pages/Error.tsx
Replaces ad-hoc error parsing with centralized buildErrorDetails function; introduces ErrorState and LocationState interfaces; refactors formatErrorDetails for unified composition.
Frontend – Error & Type Casting Updates
frontend/src/pages/AIReview.tsx, frontend/src/pages/CompareCandidates.tsx, frontend/src/pages/ExplainMatch.tsx, frontend/src/pages/InterviewQuestions.tsx, frontend/src/pages/Upload.tsx
Updates error rendering to cast as Error; removes conditional APIError checks; adds ProcessingResult type casts; adjusts error message fallbacks.
Frontend – Complex Type-Safe Refactors
frontend/src/pages/JobStatus.tsx, frontend/src/pages/Landing.tsx
JobStatus: adds RenderableObject interface, SectionData union type, PersonalData interface; refactors renderResumeSection and renderValue for type-safe nested data handling. Landing: adds Feature interface, types FEATURES array, removes as const.
Frontend – TypeScript Build Info
frontend/tsconfig.tsbuildinfo
Auto-generated TypeScript build metadata file (version 5.6.3).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Areas requiring extra attention:

  • backend/processor/views.py & backend/processor/workers/processing.py: Verify Pydantic model_dump() produces expected JSON structure for existing API consumers; confirm ProcessingResult serialization preserves nested metadata.
  • frontend/src/lib/api.ts removal & frontend/src/api/client.ts: Ensure all callsites have been migrated to new client; verify generated client matches backend OpenAPI schema expectations; check error handling coverage (new client may throw different exception types).
  • frontend/src/pages/JobStatus.tsx: Complex refactor with new type hierarchies (SectionData, PersonalData) and conditional rendering logic; verify all resume section paths are correctly typed and rendered.
  • frontend/src/pages/Error.tsx: New error state builder and formatter; ensure error display works across all error origins (navigation state, thrown errors, API responses).
  • frontend/src/services/resumeService.ts & ragService.ts: Search parameter type changes (VectorSearchQuerySchema vs SemanticSearchParams); verify request body structure matches backend expectations; confirm error responses are handled consistently.
  • backend/core/domain/resume.py: New optional legacy fields may accept stale data shapes; verify validators or migration logic prevents conflicts between legacy and canonical fields.

Possibly related PRs

  • Updated parts of frontend #3: Touches the same frontend components (SearchFilters, Error, filter components) with related type and logic updates; likely depends on or complements this PR's API client migration.
  • Added publish scripts  #6: Modifies frontend service configuration in docker-compose.yaml (env vars, build/config changes); coordinates with this PR's frontend startup command addition.

Poem

🐰 From manual fetches to schema-born types, we hop with delight!
Backend models now gleam in Pydantic's light,
OpenAPI dances, code blooms from the spec—
Auto-generated clients, no more hand-check!
Legacy fields blend with modern delight. ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.23% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The PR title 'Dev' is vague and generic, using a non-descriptive term that does not convey meaningful information about the substantial changeset. Replace the title with a specific, descriptive summary of the main change. For example: 'Migrate from Django serializers to Pydantic models and generate OpenAPI types' or 'Replace DRF serializers with Pydantic models and integrate OpenAPI client generation'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
frontend/src/pages/InterviewQuestions.tsx (1)

149-156: Same error-casting considerations as in CompareCandidates

This mirrors (error as Error).message from CompareCandidates.tsx. The approach is fine if your mutation errors are always Error-like; otherwise, consider a small fallback (e.g., instanceof Error ? error.message : String(error)) to avoid an empty message.

frontend/src/pages/ExplainMatch.tsx (1)

96-103: Consistent Error-based message rendering; same optional fallback note

You’re consistently using (error as Error).message here as well. That keeps error handling uniform across pages; just be aware that non-Error values will render as an empty span, so a tiny fallback to String(error) would improve robustness if that’s a possibility.

🧹 Nitpick comments (19)
frontend/src/pages/CompareCandidates.tsx (1)

148-155: Error casting assumes Error-like objects; consider a fallback message

Using (error as Error).message is fine if all your mutation errors are real Error (or Error-like) instances. If anything else can reach mutation.error, the span will render empty. You may want a small fallback such as error instanceof Error ? error.message : String(error) to avoid a blank error in edge cases, but not strictly required.

frontend/tsconfig.tsbuildinfo (1)

1-1: Consider removing this build artifact from version control

tsconfig.tsbuildinfo is an incremental compile artifact and usually not checked in, since it changes frequently per-developer and per-build. Recommend deleting it from the repo and adding it to .gitignore to avoid noisy diffs.

frontend/src/components/filters/EducationFilter.tsx (1)

25-42: Tighten StatusesEnum handling and normalize “Any status” comparisons

Nice move wiring EducationRequirement.statuses up to StatusesEnum[]; a couple of small refinements could improve robustness:

  1. Avoid blind casting to StatusesEnum[]
const typedStatuses = statuses as StatusesEnum[];

relies purely on trust. If levelOption.statuses ever contains an unexpected string, you’ll silently write an invalid enum value. Consider a narrowing helper instead:

const allowed: StatusesEnum[] = ['completed', 'ongoing', 'incomplete'];
const typedStatuses = statuses.filter((s): s is StatusesEnum =>
  allowed.includes(s as StatusesEnum)
);

and then keep using typedStatuses everywhere.

  1. Normalize status arrays when checking for “same selection”

The comparison

const sameStatusesSelected =
  JSON.stringify(existing.statuses?.sort()) === JSON.stringify(statuses.sort());

treats existing.statuses === null differently from statuses.length === 0, which makes it impossible to clear a level by re‑selecting “Any Status”. Normalizing both sides to arrays (and reusing typedStatuses) avoids this:

const normalize = (s: StatusesEnum[] | null | undefined) =>
  (s ?? []).slice().sort();

const sameStatusesSelected =
  JSON.stringify(normalize(existing?.statuses)) ===
  JSON.stringify(normalize(typedStatuses));

This keeps your internal state aligned with the enum while making the toggle behavior more intuitive.

Also applies to: 95-108

docker-compose.yaml (1)

171-176: Ensure wget is available in the frontend image; consider a healthcheck-based alternative

The startup sequence—waiting for /api/schema/, running generate:api, then npm run dev—is a nice way to keep the generated client in sync. One thing to confirm is that your frontend image actually has wget installed; if not, this sh -c will fail before the app starts.

If you run into that, either install wget (or switch to a tool already present in the base image), or move the readiness logic into a backend healthcheck and gate frontend with depends_on: backend: condition: service_healthy instead of an inline loop.

backend/core/domain/resume.py (2)

314-316: Same concern: potential confusion with legacy field names.

name likely maps to language.name and level to cefr or self_assessed. Without a validator to map these legacy fields to their canonical counterparts, consumers may be unsure which to use, and data could be inconsistent.

Consider adding mapping logic in accept_legacy_language:

 @model_validator(mode="before")
 @classmethod
 def accept_legacy_language(cls, v: dict):
     if isinstance(v, dict) and isinstance(v.get("language"), str):
         v["language"] = {"name": v["language"]}
+    # Map legacy 'name' to language.name
+    if isinstance(v, dict) and "name" in v and "language" not in v:
+        v["language"] = {"name": v.pop("name")}
+    # Map legacy 'level' to cefr if not present
+    if isinstance(v, dict) and "level" in v and "cefr" not in v:
+        v["cefr"] = v.get("level")
     return v

334-337: Consider adding a legacy field mapper for Award.

The title/issuer/date fields appear to be alternatives for name/organization/year. For consistency with other models that have accept_legacy_* validators, consider adding similar mapping logic.

frontend/src/services/fileConfigService.ts (1)

1-18: Tighten data/error handling around v1ConfigFileTypesRetrieve

The basic wiring to v1ConfigFileTypesRetrieve looks fine, but you might want to harden this a bit:

  • Guard against the (unlikely but possible) case where data is falsy but no error is set.
  • Prefer extracting a more informative message than String(error) if the SDK exposes one (e.g., error.message or a detail field).

For example:

export async function getFileConfig(): Promise<FileConfigResponse> {
  const { data, error } = await v1ConfigFileTypesRetrieve();

  if (error) {
    const message =
      error instanceof Error
        ? error.message
        : typeof error === 'string'
          ? error
          : JSON.stringify(error);
    throw new Error(message);
  }

  if (!data) {
    throw new Error('Empty response from file config endpoint');
  }

  return data as FileConfigResponse;
}
frontend/src/pages/Upload.tsx (1)

60-97: Ensure mutation error is typed/narrowed to something with .message

The new UI logic assumes error has a message string and calls error.message.includes(...). That’s fine at runtime if uploadResume throws standard Error instances, but TypeScript will treat error as unknown unless useResumeUpload explicitly sets TError = Error.

To keep both TS and runtime happy, consider narrowing error before using .message:

{error && (
  <div /* ... */>
    {/* ... */}
    <div /* title styles */>
      {error instanceof Error ? error.message : 'Upload failed'}
    </div>
    {error instanceof Error && error.message && (
      <div /* explanation styles */>
        {error.message.includes('email') ? (
          <>Make sure your resume contains a valid email address. The system uses email to identify and deduplicate resumes.</>
        ) : error.message.includes('already exists') ? (
          <>A resume with this email has already been uploaded. You can search for it or delete it first.</>
        ) : (
          <>Please check that your file is a valid resume document (PDF, DOCX, DOC, JPG, PNG).</>
        )}
      </div>
    )}
  </div>
)}

Alternatively (or additionally), you can type useResumeUpload as useMutation<ResumeResponse, Error, File> so error is statically known to be Error.

frontend/src/services/healthService.ts (1)

1-31: Health fetch wiring is good; consider mirroring stronger guards

The switch to v1HealthRetrieve is correct and aligned with the new client. For robustness, you might want to mirror the stronger pattern suggested for getFileConfig:

  • Derive a clearer error message than String(error).
  • Assert data is present before casting/returning to avoid silently returning undefined as HealthData.

Not strictly required, but it will make failures easier to debug and safer if the SDK behavior changes.

backend/processor/workers/processing.py (1)

89-99: Pydantic dump is appropriate; consider mode="json" if serialized directly

Using result.model_dump() is a nice cleanup versus manual dict construction and keeps serialization aligned with the Pydantic model.

If result_dict is ultimately stored/serialized as JSON (e.g., into a JSONField or message payload) and includes non-primitive types like datetime, you may want to switch to:

result_dict = result.model_dump(mode="json")

to guarantee JSON-friendly output. If downstream code already handles Python-native types correctly, the current code is fine.

frontend/src/pages/Search.tsx (1)

7-8: SearchFiltersSchema wiring looks good; a couple of minor refinements to consider

  • Type alignment: Importing and using SearchFiltersSchema from the generated client for filters keeps the UI in lockstep with the backend’s SearchFiltersSchema serializer. That’s a solid move.

  • Active filter counting: The generic Object.values(filters) + length heuristic works across arrays and strings and will correctly treat non-empty values as active. One nuance: a years_experience of 0 will be counted as an active filter even though it behaves like “no minimum”. If you’d prefer not to count that, you could special-case numeric fields:

    const activeFilterCount = Object.entries(filters).filter(([key, v]) => {
      if (v == null) return false;
      if (Array.isArray(v) || typeof v === 'string') return v.length > 0;
      if (typeof v === 'number' && key === 'years_experience') return v > 0;
      return true;
    }).length;
  • Execution time display: The cast for execution_time is safe and the <1s fallback is reasonable. If you later formalize this field in the SDK type, you can drop the cast and non-null assertion.

Also applies to: 17-18, 65-71, 89-90

frontend/src/pages/AIReview.tsx (1)

204-209: Duck-typing check for SectionFeedback is reasonable.

The runtime check for must, should, or advise properties effectively filters to SectionFeedback objects. This is a pragmatic approach when iterating over dynamic object entries.

Consider adding a type guard function for better reusability if this pattern is needed elsewhere:

function isSectionFeedback(value: unknown): value is SectionFeedback {
  const v = value as SectionFeedback;
  return v?.must !== undefined || v?.should !== undefined || v?.advise !== undefined;
}
backend/processor/views.py (1)

166-166: Consider using Request type instead of Any.

The Any type annotation loses type information. Since this is a sync method (not async), and other sync views might use the standard DRF Request, consider keeping the type specific:

-    def get(self, request: Any) -> Response:
+    def get(self, request: Request) -> Response:
backend/core/domain/processing.py (1)

26-31: Consider making resume optional or providing a mechanism for partial results.

The resume field is required with no default, meaning a ProcessingResult cannot be instantiated without a fully parsed Resume. If processing fails mid-way, this could complicate error handling. Consider whether resume: Resume | None = None would better support partial failure states.

frontend/src/services/resumeService.ts (2)

16-31: Inconsistent API call pattern: uploadResume uses raw fetch while other functions use the generated SDK.

This creates a maintenance burden—if the API base URL or authentication changes, this function must be updated separately. Consider using the generated SDK's upload function if available, or at minimum, document why raw fetch is necessary here (e.g., multipart/form-data handling).


33-37: Error handling loses type information.

String(error) coerces the error to a string, losing structured error details that may be useful for debugging or user feedback. Consider preserving the error structure:

 export async function getResumeStatus(uid: string): Promise<ResumeResponse> {
   const { data, error } = await v1ResumesRetrieve2({ path: { uid } });
-  if (error) throw new Error(String(error));
+  if (error) throw error;
   return data as ResumeResponse;
 }

Or extract meaningful details if the error is an object with message or detail fields.

frontend/src/pages/JobStatus.tsx (3)

269-281: Move interface definitions outside the component.

PersonalData and SectionData are defined inside the JobStatus component. This causes them to be re-created on every render (though the runtime cost is negligible) and prevents reuse. Move them to module scope for clarity.


283-289: The isEmpty check has the same fragile array detection issue.

Lines 287-288 repeat the problematic array check pattern. Use Array.isArray() for clarity:

-    const asArray = data as unknown[];
-    const asRecord = data as Record<string, unknown>;
     const isEmpty = !data ||
-                   (asArray.length !== undefined && asArray.length === 0) ||
-                   (asRecord && Object.keys(asRecord).length === 0 && asArray.length === undefined);
+                   (Array.isArray(data) && data.length === 0) ||
+                   (!Array.isArray(data) && typeof data === 'object' && Object.keys(data).length === 0);

2028-2037: Same isEmpty logic concern applies here.

This duplicates the fragile array detection logic from renderResumeSection. Consider extracting a shared isEmpty utility function that uses Array.isArray() for consistency and maintainability.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3f24b68 and a584aa6.

⛔ Files ignored due to path filters (5)
  • frontend/package-lock.json is excluded by !**/package-lock.json
  • frontend/src/api/generated/index.ts is excluded by !**/generated/**
  • frontend/src/api/generated/sdk.gen.ts is excluded by !**/generated/**
  • frontend/src/api/generated/types.gen.ts is excluded by !**/generated/**
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (35)
  • backend/core/domain/processing.py (2 hunks)
  • backend/core/domain/resume.py (6 hunks)
  • backend/processor/serializers.py (0 hunks)
  • backend/processor/views.py (5 hunks)
  • backend/processor/workers/processing.py (1 hunks)
  • backend/search/serializers.py (2 hunks)
  • docker-compose.yaml (1 hunks)
  • frontend/openapi-ts.config.ts (1 hunks)
  • frontend/package.json (2 hunks)
  • frontend/src/api/client.ts (1 hunks)
  • frontend/src/components/ResultCard.tsx (1 hunks)
  • frontend/src/components/SearchFilters.tsx (2 hunks)
  • frontend/src/components/filters/ActiveFiltersBar.tsx (1 hunks)
  • frontend/src/components/filters/EducationFilter.tsx (4 hunks)
  • frontend/src/components/filters/LanguagesFilter.tsx (1 hunks)
  • frontend/src/components/filters/LocationsFilter.tsx (1 hunks)
  • frontend/src/hooks/useResumeSearch.ts (2 hunks)
  • frontend/src/hooks/useResumeUpload.ts (1 hunks)
  • frontend/src/lib/api.ts (0 hunks)
  • frontend/src/pages/AIReview.tsx (5 hunks)
  • frontend/src/pages/CompareCandidates.tsx (1 hunks)
  • frontend/src/pages/Error.tsx (1 hunks)
  • frontend/src/pages/ExplainMatch.tsx (1 hunks)
  • frontend/src/pages/Health.tsx (1 hunks)
  • frontend/src/pages/InterviewQuestions.tsx (1 hunks)
  • frontend/src/pages/JobStatus.tsx (16 hunks)
  • frontend/src/pages/Landing.tsx (3 hunks)
  • frontend/src/pages/PrivacyPolicy.tsx (1 hunks)
  • frontend/src/pages/Search.tsx (4 hunks)
  • frontend/src/pages/Upload.tsx (1 hunks)
  • frontend/src/services/fileConfigService.ts (2 hunks)
  • frontend/src/services/healthService.ts (2 hunks)
  • frontend/src/services/ragService.ts (3 hunks)
  • frontend/src/services/resumeService.ts (1 hunks)
  • frontend/tsconfig.tsbuildinfo (1 hunks)
💤 Files with no reviewable changes (2)
  • frontend/src/lib/api.ts
  • backend/processor/serializers.py
🧰 Additional context used
🧬 Code graph analysis (16)
frontend/src/pages/InterviewQuestions.tsx (1)
frontend/src/pages/Error.tsx (1)
  • Error (66-124)
frontend/src/components/filters/ActiveFiltersBar.tsx (2)
backend/search/serializers.py (1)
  • SearchFiltersSchema (117-160)
frontend/src/api/generated/types.gen.ts (1)
  • SearchFiltersSchema (559-588)
frontend/src/components/SearchFilters.tsx (2)
backend/search/serializers.py (1)
  • SearchFiltersSchema (117-160)
frontend/src/api/generated/types.gen.ts (1)
  • SearchFiltersSchema (559-588)
frontend/src/services/fileConfigService.ts (1)
frontend/src/api/generated/sdk.gen.ts (1)
  • v1ConfigFileTypesRetrieve (11-16)
frontend/src/pages/Search.tsx (2)
backend/search/serializers.py (1)
  • SearchFiltersSchema (117-160)
frontend/src/api/generated/types.gen.ts (1)
  • SearchFiltersSchema (559-588)
frontend/src/api/client.ts (1)
frontend/src/api/generated/sdk.gen.ts (1)
  • client (6-6)
frontend/src/hooks/useResumeSearch.ts (2)
backend/search/serializers.py (3)
  • VectorSearchQuerySchema (163-188)
  • GraphSearchQuerySchema (191-205)
  • HybridSearchQuerySchema (208-230)
frontend/src/api/generated/types.gen.ts (3)
  • VectorSearchQuerySchema (707-728)
  • GraphSearchQuerySchema (235-248)
  • HybridSearchQuerySchema (250-267)
frontend/src/components/filters/EducationFilter.tsx (1)
frontend/src/api/generated/types.gen.ts (1)
  • StatusesEnum (705-705)
frontend/src/pages/CompareCandidates.tsx (1)
frontend/src/pages/Error.tsx (1)
  • Error (66-124)
frontend/src/pages/ExplainMatch.tsx (1)
frontend/src/pages/Error.tsx (1)
  • Error (66-124)
backend/processor/views.py (3)
backend/core/domain/processing.py (1)
  • ResumeResponse (34-43)
frontend/src/api/generated/types.gen.ts (2)
  • ResumeResponse (511-528)
  • ResumeListResponse (489-506)
backend/processor/serializers.py (1)
  • FileUploadSerializer (24-65)
frontend/src/services/resumeService.ts (4)
backend/core/domain/processing.py (1)
  • ResumeResponse (34-43)
frontend/src/api/generated/types.gen.ts (1)
  • ResumeResponse (511-528)
frontend/src/api/client.ts (1)
  • API_BASE_URL (4-4)
frontend/src/api/generated/sdk.gen.ts (5)
  • v1ResumesRetrieve2 (91-96)
  • v1SearchSemanticCreate (131-136)
  • v1SearchStructuredCreate (141-146)
  • v1SearchHybridCreate (121-126)
  • v1FiltersRetrieve (21-26)
frontend/src/services/healthService.ts (1)
frontend/src/api/generated/sdk.gen.ts (1)
  • v1HealthRetrieve (31-36)
backend/core/domain/resume.py (2)
frontend/src/api/generated/types.gen.ts (2)
  • EducationStatus (167-167)
  • Location (370-374)
backend/core/models/neo4j_models.py (1)
  • EducationStatus (28-31)
frontend/src/services/ragService.ts (2)
frontend/src/api/generated/types.gen.ts (3)
  • ExplainMatchRequest (188-191)
  • CompareCandidatesRequest (30-34)
  • InterviewQuestionsRequest (273-278)
frontend/src/api/generated/sdk.gen.ts (3)
  • v1RagExplainMatchCreate (51-56)
  • v1RagCompareCreate (41-46)
  • v1RagInterviewQuestionsCreate (61-66)
backend/core/domain/processing.py (2)
backend/core/domain/resume.py (1)
  • Resume (362-414)
frontend/src/api/generated/types.gen.ts (5)
  • Resume (463-484)
  • ReviewResult (530-544)
  • ProcessingMetadata (425-437)
  • ProcessingResult (442-446)
  • ResumeResponse (511-528)
🪛 Biome (2.1.2)
frontend/src/pages/Error.tsx

[error] 66-66: Do not shadow the global "Error" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (36)
frontend/package.json (1)

6-12: OpenAPI client tooling wiring looks consistent with the new workflow

The generate:api script and the @hey-api/* dependencies line up with openapi-ts.config.ts and the docker-compose startup flow. This should give you a clean, reproducible client surface as long as openapi-ts picks up the config correctly.

Please confirm locally that npm run generate:api succeeds (using your backend’s schema URL) and regenerates src/api/generated as expected in both Docker and non-Docker dev environments.

Also applies to: 14-15, 27-27

frontend/openapi-ts.config.ts (1)

1-17: OpenAPI config matches the new client toolchain

The config is coherent with the new scripts and Docker flow: OPENAPI_URL override for containers, localhost default for local dev, and @hey-api/client-fetch + types/sdk plugins targeting src/api/generated. This should be a solid base for codegen.

Please double-check that:

  • Running openapi-ts (via npm run generate:api) picks up this config file, and
  • The generated client compiles cleanly against your current backend schema (no missing/renamed fields).
backend/search/serializers.py (2)

34-62: Well-structured serializers for search results.

The new EducationEntrySerializer, LanguageEntrySerializer, and LocationEntrySerializer properly encapsulate nested data structures with appropriate field types and help text. This improves API contract clarity compared to the previous DictField/ListField approach.


75-83: LGTM!

The integration of the new serializers into SearchResultSerializer is correct. The many=True flag is appropriately used for list fields (education, languages), and all fields are properly marked as optional/nullable for search result flexibility.

backend/core/domain/resume.py (4)

66-69: Potential data inconsistency between canonical and legacy fields.

The Contact model now has linkedin, github, website as direct fields alongside links.linkedin, links.github. This creates two sources of truth for the same data. If both are populated with different values, it's unclear which takes precedence.

Consider adding a model_validator to either:

  1. Sync legacy fields from nested links on read
  2. Merge/prefer one source when both are provided
# Example sync validator approach:
@model_validator(mode="after")
def sync_legacy_links(self) -> "Contact":
    if self.links:
        if self.linkedin is None and self.links.linkedin:
            self.linkedin = self.links.linkedin
        if self.github is None and self.links.github:
            self.github = self.links.github
    return self

202-202: LGTM!

Adding an optional description field to Project is a reasonable enhancement for providing brief overviews alongside the existing key_points.


239-241: LGTM!

The year field provides a simpler alternative to start/end dates for graduation year, and gpa as a string type appropriately handles various formats (numeric, fractional, or text-based grading systems).


375-383: Populate legacy flat fields from canonical nested sources, or document their non-population.

The Resume model has legacy flat fields (name, email, phone, location, linkedin, github, website, resume_lang) that duplicate data from personal_info and its nested structures. The @model_validator at line 385 handles backward-compatibility conversions for incoming data but does not auto-populate these flat fields from the canonical nested sources. They remain None unless explicitly set in the input data.

Either:

  1. Add validator logic to sync personal_info → legacy fields after initialization, or
  2. Remove these fields if they're genuinely unused, or
  3. Clearly document that these fields are not auto-synced and explain when/why callers should populate them.

This prevents inconsistency where personal_info.name has a value but name is None.

frontend/src/components/ResultCard.tsx (1)

3-3: LGTM!

The import path update aligns with the broader migration to the auto-generated OpenAPI TypeScript client.

frontend/src/components/filters/LocationsFilter.tsx (1)

2-2: LGTM!

Import path updated to use the new API client module, consistent with other components in this migration.

frontend/src/pages/Health.tsx (1)

2-2: LGTM!

Import path updated for API_BASE_URL to align with the centralized API client module.

frontend/src/hooks/useResumeUpload.ts (1)

2-7: Import cleanup and hook wiring look correct

useResumeUpload still correctly forwards to uploadResume via useMutation, and the remaining import is the right runtime dependency. No issues here.

frontend/src/components/filters/LanguagesFilter.tsx (1)

3-53: LanguageRequirement import source aligns with new API client

Switching LanguageRequirement to come from ../../api/client matches the new centralized API types, and the component’s usage (language, min_cefr) remains consistent with the filter schema. Looks good.

frontend/src/pages/PrivacyPolicy.tsx (1)

4-52: API_BASE_URL import swap is consistent and non-breaking

Using API_BASE_URL from ../api/client keeps this page aligned with the new centralized API client. The docs link construction remains the same; no behavior change.

frontend/src/api/client.ts (1)

1-15: Clean API client configuration pattern.

The setup correctly configures the generated client singleton with the base URL from environment variables and provides a centralized export point. The barrel export (export * from './generated') ensures consumers only need to import from ../api/client.

frontend/src/components/SearchFilters.tsx (1)

2-14: Type migration to generated schema looks correct.

The SearchFiltersSchema from the generated API client matches the expected shape, and the component's usage of optional fields with nullish coalescing (??) is appropriate for the nullable schema properties.

frontend/src/pages/AIReview.tsx (1)

48-48: Type assertion is appropriate given the status guard.

The cast to ProcessingResult is safe here since it's guarded by the job?.status === "completed" condition, which guarantees the result field is populated per the API contract.

frontend/src/pages/Landing.tsx (2)

3-3: Import path updated to new API client module.


41-80: Good addition of explicit Feature typing.

Defining the Feature interface improves type safety and documentation for the layout flags. The typed array allows the bento grid to conditionally apply CSS classes based on wide, tall, and small properties.

frontend/src/components/filters/ActiveFiltersBar.tsx (1)

1-5: Type migration consistent with SearchFilters component.

The import change and prop type update align with the centralized API client types. The component's field access patterns are compatible with the nullable schema properties.

frontend/src/hooks/useResumeSearch.ts (1)

8-25: Type migration to generated schemas is correct.

The UnifiedSearchParams discriminated union using the new schema types maintains the same structure. The intersection with { type: "semantic" | "structured" | "hybrid" } provides proper type narrowing in the mutation function.

backend/processor/views.py (2)

27-34: Clean Pydantic model for paginated response schema.

The ResumeListResponse model properly documents the pagination structure for OpenAPI generation, matching the frontend's ResumeListResponse type from types.gen.ts.


47-47: OpenAPI schema references updated to Pydantic models.

Using ResumeListResponse and ResumeResponse Pydantic models in extend_schema responses enables drf-spectacular to generate accurate TypeScript types for the frontend client.

Also applies to: 70-70, 93-93

frontend/src/pages/Error.tsx (2)

5-27: Well-structured error state normalization.

The ErrorState interface handles various error shapes from different sources (axios-style with response, fetch-style with status/statusCode, etc.), and buildErrorDetails consolidates them into a consistent format for display.


29-38: Consolidating error parsing improves maintainability.

The buildErrorDetails function with fallback chains (error.status ?? error.statusCode, error.data ?? error.response) handles the variety of error formats encountered in practice.

backend/core/domain/processing.py (4)

1-4: LGTM!

Clean imports using modern Python typing features and Pydantic for model definitions.


10-23: LGTM!

ProcessingMetadata is well-structured with sensible defaults for all fields. The optional error fields appropriately use str | None for cases where errors may or may not occur.


34-43: LGTM!

ResumeResponse aligns well with the generated TypeScript type (frontend/src/api/generated/types.gen.ts lines 510-527). The Literal type for status provides good type safety, and Field descriptions will generate useful OpenAPI documentation.


46-49: LGTM!

EmbeddingTextData as a typed NamedTuple is appropriate for this simple data structure.

frontend/src/services/resumeService.ts (2)

1-14: LGTM!

Clean import structure from the centralized API client, properly importing both types and SDK functions.


39-61: LGTM with a note on type assertions.

The search functions and getFilterOptions follow a consistent pattern. The type assertions (data as SearchResponse, data as FilterOptions) are acceptable given the SDK's typing, but verify that the generated types match the actual API responses to avoid runtime surprises.

frontend/src/services/ragService.ts (3)

1-8: LGTM!

Clean imports from the centralized API client, using the generated SDK functions and request types.


92-108: LGTM with same error handling note as resumeService.ts.

The three RAG functions follow the established pattern. The same String(error) concern applies here—consider preserving error structure for better debugging.


10-90: Verify whether generated types exist in the project's OpenAPI infrastructure.

These response interfaces (JobMatchExplanation, CandidateComparison, InterviewQuestionSet, etc.) appear to duplicate types that should be auto-generated from the OpenAPI spec. Using generated types ensures frontend-backend alignment and reduces maintenance burden when the API changes.

If the generated types are incomplete or incorrect, fix the OpenAPI spec rather than maintaining parallel definitions. If no generated types infrastructure exists or the types are not available through code generation, maintaining local interface definitions is the appropriate approach.

frontend/src/pages/JobStatus.tsx (2)

4-5: LGTM!

Good migration to import types from the centralized API client, ensuring type alignment with the generated OpenAPI types.


1355-1358: LGTM!

The error casting (error as Error).message is a reasonable approach given the hook's return type.

${dataBlock}${stackBlock}`;
}

export default function Error() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find usages of the Error component to verify route/import naming
echo "=== Searching for imports of Error component ==="
rg -n "from.*Error" --type=ts --type=tsx -g '!Error.tsx' frontend/ 2>/dev/null | head -20
echo ""
echo "=== Searching for import statements ==="
rg -n "import.*Error" --type=ts --type=tsx -g '!Error.tsx' frontend/ 2>/dev/null | head -20
echo ""
echo "=== Checking Error.tsx for usage of global Error ==="
cat -n frontend/src/pages/Error.tsx

Repository: HardMax71/ResuMariner

Length of output: 4549


🏁 Script executed:

#!/bin/bash
# Look for router configuration
echo "=== Searching for router configuration ==="
find frontend/src -name "*route*" -o -name "*router*" -o -name "*App*" | head -10
echo ""
echo "=== Check App files ==="
find frontend/src -maxdepth 2 -name "App.*" | xargs cat -n 2>/dev/null
echo ""
echo "=== Check for any Error reference in config files ==="
rg -n "Error" frontend/src --type=ts --type=tsx -B2 -A2 | grep -E "(route|Route|import|Error)" | head -30

Repository: HardMax71/ResuMariner

Length of output: 2446


Rename component to avoid shadowing global Error.

The component name Error shadows the global Error constructor. While this module doesn't directly use the native Error class, the shadowing can cause confusion and potential issues in future maintenance. Rename the component to ErrorPage.

Update the import and route reference in frontend/src/App.tsx (lines 11 and 38).

🧰 Tools
🪛 Biome (2.1.2)

[error] 66-66: Do not shadow the global "Error" property.

Consider renaming this variable. It's easy to confuse the origin of variables when they're named after a known global.

(lint/suspicious/noShadowRestrictedNames)

🤖 Prompt for AI Agents
In frontend/src/pages/Error.tsx around line 66 the React component is named
Error which shadows the global Error constructor; rename the component to
ErrorPage (update the component declaration and any internal references/exports
in this file) and then update the import and route usage in frontend/src/App.tsx
(change the import at line 11 to import ErrorPage and update the route reference
at line 38 to use ErrorPage) so no global name is shadowed.

Comment on lines +27 to 55
const renderValue = (value: unknown): string => {
if (value === null || value === undefined) return "—";
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
return String(value);

// Primitives - use String() which handles all primitives
const primitiveResult = String(value);
if (primitiveResult !== "[object Object]" && !primitiveResult.startsWith("[object")) {
return primitiveResult;
}
if (typeof value === "object") {
// Handle common nested structures
if (value.city || value.country) return [value.city, value.country].filter(Boolean).join(", ");
if (value.name) return value.name;
if (value.title) return value.title;
if (value.value) return value.value;
// For arrays, join them
if (Array.isArray(value)) return value.map(renderValue).join(", ");
// Otherwise stringify
return JSON.stringify(value);

// Object handling via type assertion
const obj = value as RenderableObject | unknown[];

// Array handling - check for length property and numeric indexing
const asArray = obj as unknown[];
if (asArray.length !== undefined && asArray[0] !== undefined || (obj as unknown[]).length === 0) {
return (obj as unknown[]).map(renderValue).join(", ");
}
return String(value);

// Object with known properties
const asObj = obj as RenderableObject;
if (asObj.city || asObj.country) return [asObj.city, asObj.country].filter(Boolean).join(", ");
if (asObj.name) return asObj.name;
if (asObj.title) return asObj.title;
if (asObj.text) return asObj.text;
if (asObj.value) return asObj.value;

// Fallback to JSON
return JSON.stringify(value);
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fragile array detection logic.

The array check on line 41 is error-prone:

if (asArray.length !== undefined && asArray[0] !== undefined || (obj as unknown[]).length === 0)

This condition has operator precedence issues (&& binds tighter than ||) and checking asArray[0] !== undefined incorrectly excludes arrays like [undefined, 1, 2]. Consider using Array.isArray():

-  const asArray = obj as unknown[];
-  if (asArray.length !== undefined && asArray[0] !== undefined || (obj as unknown[]).length === 0) {
-    return (obj as unknown[]).map(renderValue).join(", ");
+  if (Array.isArray(obj)) {
+    return obj.map(renderValue).join(", ");
   }
🤖 Prompt for AI Agents
In frontend/src/pages/JobStatus.tsx around lines 27 to 55, the array detection
logic is fragile and wrong due to operator precedence and checking asArray[0]
which excludes valid arrays with undefined first elements; replace that block
with a proper Array.isArray(value) guard (or Array.isArray(obj)) to detect
arrays, handle empty arrays explicitly, and map/renderValue over array elements
then join with ", " — remove the current length/element checks and use the
Array.isArray type guard so arrays (including ones with undefined entries) are
correctly processed.

@HardMax71 HardMax71 merged commit f6d47b8 into main Dec 6, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant