Skip to content

Comments

Feat/dp 575#1

Open
maddy-a wants to merge 3 commits intomainfrom
feat/DP-575
Open

Feat/dp 575#1
maddy-a wants to merge 3 commits intomainfrom
feat/DP-575

Conversation

@maddy-a
Copy link
Contributor

@maddy-a maddy-a commented Oct 10, 2024

Summary by CodeRabbit

  • New Features

    • Updated package import path for consistency.
    • Introduced a new "Search API Usage" section in the documentation with examples.
    • Added search functionality, including a new method for performing searches and customizable options.
  • Documentation

    • Enhanced README with additional usage examples and clarifications for the Search API.

@coderabbitai
Copy link

coderabbitai bot commented Oct 10, 2024

📝 Walkthrough

Walkthrough

The changes in this pull request primarily involve updates to the README.md file and the dappier.go implementation. The README.md file reflects a new package import path and introduces a "Search API Usage" section with examples. In dappier.go, significant modifications include the addition of search functionality, renaming of the AiRecommendationsRequest struct to SearchRequest, and the introduction of new types and methods related to search operations. These updates aim to enhance the library's documentation and functionality.

Changes

File Change Summary
README.md Updated package import path from github.com/DappierAI/dappier-go to github.com/dappier/dappier-go. Added "Search API Usage" section with examples.
dappier.go Added search functionality: defined SearchBaseUrl, renamed AiRecommendationsRequest to SearchRequest, added SearchResponse and SearchResult types, modified AIRecommendations method, and introduced a new Search method. New functional options for search requests were also defined.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant DappierApp
    participant API

    User->>DappierApp: Search(query, dataModelID, opts)
    DappierApp->>API: Send search request
    API-->>DappierApp: Return search results
    DappierApp-->>User: Provide search results
Loading

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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: 9

🧹 Outside diff range comments (5)
README.md (1)

Line range hint 1-218: Address markdown formatting issues

To improve the overall consistency and readability of the document, please address the following markdown formatting issues:

  1. Replace hard tabs with spaces for consistent indentation (lines 32, 66, and potentially others).
  2. Remove extra blank lines to maintain a single blank line between sections (e.g., line 142).
  3. Ensure all headings are surrounded by blank lines for better readability.
  4. Remove trailing punctuation (colons) from headings in the Parameters section.
  5. Add blank lines before and after lists for better separation.

These changes will enhance the document's overall structure and adherence to common markdown best practices.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~204-~204: A determiner appears to be missing. Consider inserting it.
Context: ...r): - The number of articles to return. Default is 9. ### ref (string): - The domain...

(AI_EN_LECTOR_MISSING_DETERMINER)


[uncategorized] ~217-~217: Loose punctuation mark.
Context: ...o retrieve articles. - "most_recent": Retrieves articles sorted by the most r...

(UNLIKELY_OPENING_PUNCTUATION)

🪛 Markdownlint

142-142: Expected: 1; Actual: 2
Multiple consecutive blank lines

(MD012, no-multiple-blanks)


199-199: Expected: 1; Actual: 0; Below
Headings should be surrounded by blank lines

(MD022, blanks-around-headings)


203-203: Expected: 1; Actual: 0; Below
Headings should be surrounded by blank lines

(MD022, blanks-around-headings)


206-206: Expected: 1; Actual: 0; Below
Headings should be surrounded by blank lines

(MD022, blanks-around-headings)


210-210: Expected: 1; Actual: 0; Below
Headings should be surrounded by blank lines

(MD022, blanks-around-headings)


214-214: Expected: 1; Actual: 0; Below
Headings should be surrounded by blank lines

(MD022, blanks-around-headings)


197-197: null
Multiple headings with the same content

(MD024, no-duplicate-heading)


199-199: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


203-203: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


206-206: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


210-210: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


214-214: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


200-200: null
Lists should be surrounded by blank lines

(MD032, blanks-around-lists)


204-204: null
Lists should be surrounded by blank lines

(MD032, blanks-around-lists)


207-207: null
Lists should be surrounded by blank lines

(MD032, blanks-around-lists)


211-211: null
Lists should be surrounded by blank lines

(MD032, blanks-around-lists)


215-215: null
Lists should be surrounded by blank lines

(MD032, blanks-around-lists)

dappier.go (4)

Line range hint 209-223: Use Appropriate Request Struct in AIRecommendations

The AIRecommendations method is currently using SearchRequest as its payload, which might not accurately represent the required fields for AI recommendations. Define a dedicated AiRecommendationsRequest struct to ensure the request payload matches the API's expectations and to improve code clarity.

Example:

type AiRecommendationsRequest struct {
	Query          string `json:"query"`
	SimilarityTopK int    `json:"similarity_top_k"`
	Ref            string `json:"ref"`
	NumArticlesRef int    `json:"num_articles_ref"`
	// Add any other fields specific to AI recommendations
}

Update the method to use this struct:

- requestData := SearchRequest{
+ requestData := AiRecommendationsRequest{

Line range hint 209-223: Apply DRY Principle to Reduce Duplicate Code

The AIRecommendations and Search methods contain duplicate code for setting up HTTP requests and handling responses. Refactor common functionality into helper methods to adhere to the DRY (Don't Repeat Yourself) principle, improving maintainability.

Example:

  • Create a helper function to handle HTTP request creation and response parsing.
  • Parameters can include the URL, request payload, and expected response structure.

Also applies to: 301-366


Line range hint 14-366: Concurrency Considerations for DappierApp

If instances of DappierApp are intended to be used concurrently across multiple goroutines, ensure that shared resources (like the http.Client or any internal state) are safe for concurrent access.

Consider documenting the concurrency guarantees or adding synchronization as needed.


Line range hint 209-223: Handle Non-OK HTTP Status Codes Gracefully

In the AIRecommendations method, when a non-OK HTTP status is received, only the status code is returned in the error. Consider including the response body or additional context to aid in debugging.

Example:

if resp.StatusCode != http.StatusOK {
+   body, _ := io.ReadAll(resp.Body)
	return nil, fmt.Errorf("received non-OK response status: %d, body: %s", resp.StatusCode, body)
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE

📥 Commits

Files that changed from the base of the PR and between 421d42e and 210a6c7.

📒 Files selected for processing (2)
  • README.md (4 hunks)
  • dappier.go (4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
dappier.go (1)

Pattern **/*.go: Be sure that the code is optimized to enable concurrency, efficient use of threading and follows the standard go guide, always point out all possible unhandled error case and suggest code snippets to improve key areas such as:
- Logic
- Security
- Performance
- Data races
- Consistency
- Error handling
- Maintainability
- Modularity
- Complexity
- Optimization
- Best practices: DRY, SOLID, KISS

🪛 Markdownlint
README.md

32-32: Column: 1
Hard tabs

(MD010, no-hard-tabs)


66-66: Column: 1
Hard tabs

(MD010, no-hard-tabs)


142-142: Expected: 1; Actual: 2
Multiple consecutive blank lines

(MD012, no-multiple-blanks)


199-199: Expected: 1; Actual: 0; Below
Headings should be surrounded by blank lines

(MD022, blanks-around-headings)


203-203: Expected: 1; Actual: 0; Below
Headings should be surrounded by blank lines

(MD022, blanks-around-headings)


206-206: Expected: 1; Actual: 0; Below
Headings should be surrounded by blank lines

(MD022, blanks-around-headings)


210-210: Expected: 1; Actual: 0; Below
Headings should be surrounded by blank lines

(MD022, blanks-around-headings)


214-214: Expected: 1; Actual: 0; Below
Headings should be surrounded by blank lines

(MD022, blanks-around-headings)


197-197: null
Multiple headings with the same content

(MD024, no-duplicate-heading)


199-199: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


203-203: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


206-206: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


210-210: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


214-214: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


200-200: null
Lists should be surrounded by blank lines

(MD032, blanks-around-lists)


204-204: null
Lists should be surrounded by blank lines

(MD032, blanks-around-lists)


207-207: null
Lists should be surrounded by blank lines

(MD032, blanks-around-lists)


211-211: null
Lists should be surrounded by blank lines

(MD032, blanks-around-lists)


215-215: null
Lists should be surrounded by blank lines

(MD032, blanks-around-lists)

🪛 LanguageTool
README.md

[uncategorized] ~204-~204: A determiner appears to be missing. Consider inserting it.
Context: ...r): - The number of articles to return. Default is 9. ### ref (string): - The domain...

(AI_EN_LECTOR_MISSING_DETERMINER)


[uncategorized] ~217-~217: Loose punctuation mark.
Context: ...o retrieve articles. - "most_recent": Retrieves articles sorted by the most r...

(UNLIKELY_OPENING_PUNCTUATION)

🔇 Additional comments (2)
README.md (1)

14-14: Verify consistency of import path change

The package import path has been updated from github.com/DappierAI/dappier-go to github.com/dappier/dappier-go. This change appears to be consistent across all code examples in the file. However, it's crucial to ensure this change is applied uniformly throughout the entire codebase and documentation.

To confirm the consistency of this change, please run the following script:

If the script returns any results, those instances should be updated to the new import path.

Also applies to: 32-32, 66-66, 155-155

✅ Verification successful

Import path change is consistent

All instances of the old import path github.com/DappierAI/dappier-go have been successfully updated to github.com/dappier/dappier-go across documentation and source files. No remaining instances found.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for any remaining instances of the old import path

# Test: Search for any remaining instances of the old import path
rg --type md "github.com/DappierAI/dappier-go"

Length of output: 46


Script:

#!/bin/bash
# Description: Corrected script to check for any remaining instances of the old import path in Markdown files

# Test: Search for any remaining instances of the old import path in README.md and other Markdown files
rg --type md "github.com/DappierAI/dappier-go"

Length of output: 46


Script:

#!/bin/bash
# Description: Search for any remaining instances of the old import path across all relevant file types

# Test: Search for the old import path in source code and documentation files
rg "github.com/DappierAI/dappier-go" --type py --type js --type go --type md --type txt

Length of output: 87

dappier.go (1)

327-328: ⚠️ Potential issue

Validate URL Parameter Encoding

When constructing the URL for the search request, ensure that dataModelID is properly URL-encoded to handle any special characters.

- url := fmt.Sprintf("%s?data_model_id=%s", SearchBaseUrl, dataModelID)
+ url := fmt.Sprintf("%s?data_model_id=%s", SearchBaseUrl, url.QueryEscape(dataModelID))

Alternatively, use the net/url package to build query parameters.

Would you like assistance in updating the URL construction?

Comment on lines +143 to +195
## Search API Usage

The Search API allows you to query with different data_model_ids, enabling flexible data search across different models.

Here’s how to use the Search API:

```go
package main

import (
"fmt"
"log"
"github.com/dappier/dappier-go"
)

func main() {
// Initialize Dappier client
client, err := dappier.NewDappierApp("your-api-key")
if err != nil {
log.Fatalf("Failed to initialize Dappier client: %v", err)
}

// Example: Search with default options
searchResult, err := client.Search("Latest Microsoft News", "dm_01htjq2njgecvah7ncepm8v87y")
if err != nil {
log.Fatalf("Failed to get search results: %v", err)
}

// Print the results
for _, result := range searchResult.Response.Results {
fmt.Printf("Search Result - %v\n", result)
}

// Example: Search with custom options
searchCustomResult, err := client.Search(
"Latest Microsoft News",
"dm_01htjq2njgecvah7ncepm8v87y",
dappier.WithSearchSimilarityTopK(6), // Custom similarity_top_k
dappier.WithSearchRef("familyproof.com"), // Custom ref
dappier.WithSearchNumArticlesRef(3), // Ensure 3 articles from the domain
)
if err != nil {
log.Fatalf("Failed to get search results: %v", err)
}

// Print the custom results
for _, result := range searchCustomResult.Response.Results {
fmt.Printf("Search Result (Custom) - %v\n", result)
}
}


```
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Approve addition of Search API Usage section with minor suggestion

The new "Search API Usage" section is a valuable addition to the documentation. It provides clear explanations and code examples for both default and custom usage of the Search API. The structure and style are consistent with the existing "AI Recommendations API Usage" section, which is excellent for maintaining documentation coherence.

To further improve clarity, consider adding a brief explanation of the data_model_id parameter in the introductory paragraph or as a comment in the code example. This would help users understand the significance of this parameter when using the Search API.

Comment on lines +149 to +195
```go
package main

import (
"fmt"
"log"
"github.com/dappier/dappier-go"
)

func main() {
// Initialize Dappier client
client, err := dappier.NewDappierApp("your-api-key")
if err != nil {
log.Fatalf("Failed to initialize Dappier client: %v", err)
}

// Example: Search with default options
searchResult, err := client.Search("Latest Microsoft News", "dm_01htjq2njgecvah7ncepm8v87y")
if err != nil {
log.Fatalf("Failed to get search results: %v", err)
}

// Print the results
for _, result := range searchResult.Response.Results {
fmt.Printf("Search Result - %v\n", result)
}

// Example: Search with custom options
searchCustomResult, err := client.Search(
"Latest Microsoft News",
"dm_01htjq2njgecvah7ncepm8v87y",
dappier.WithSearchSimilarityTopK(6), // Custom similarity_top_k
dappier.WithSearchRef("familyproof.com"), // Custom ref
dappier.WithSearchNumArticlesRef(3), // Ensure 3 articles from the domain
)
if err != nil {
log.Fatalf("Failed to get search results: %v", err)
}

// Print the custom results
for _, result := range searchCustomResult.Response.Results {
fmt.Printf("Search Result (Custom) - %v\n", result)
}
}


```
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Approve code examples with suggestion for consistency

The code examples for the Search API Usage are clear, demonstrative, and follow good practices such as proper error handling. They effectively show how to use both default and custom options when making Search API requests.

For consistency with the AI Recommendations API example, consider adding a brief comment explaining each custom option in the Search API example. This would help users understand the purpose of each parameter more easily.

Example of suggested addition:

searchCustomResult, err := client.Search(
  "Latest Microsoft News",
  "dm_01htjq2njgecvah7ncepm8v87y",
  dappier.WithSearchSimilarityTopK(6),    // Set custom number of results to return
  dappier.WithSearchRef("familyproof.com"), // Set custom reference domain
  dappier.WithSearchNumArticlesRef(3),    // Ensure 3 articles from the reference domain
)

Comment on lines +197 to +218
## Parameters

### `query` (string):
- A natural language query or URL.
- If a URL is passed, the AI analyzes the page, creates a summary, and performs a semantic search query based on the content.

### `similarity_top_k` (integer):
- The number of articles to return. Default is 9.

### `ref` (string):
- The domain of the site from which the recommendations should come.
- Example: `techcrunch.com`.

### `num_articles_ref` (integer):
- Specifies how many articles should be guaranteed to match the domain specified in `ref`.
- Use this to ensure a set number of articles from the desired domain appear in the results.

### `search_algorithm` (string):
- Options: `"most_recent"` or `"semantic"`.
- `"semantic"` (default): Contextual matching of the query to retrieve articles.
- `"most_recent"`: Retrieves articles sorted by the most recent publication date.

Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Improve formatting of Search API Parameters section

The Parameters section for the Search API provides clear and consistent descriptions with the AI Recommendations API section. However, to maintain visual consistency throughout the document, consider applying the following formatting improvements:

  1. Add a blank line before and after each parameter heading.
  2. Remove the colon (:) from the end of each parameter heading.
  3. Ensure consistent indentation for list items under each parameter.

Here's an example of the suggested formatting:

## Parameters

### `query` (string)

- A natural language query or URL.
- If a URL is passed, the AI analyzes the page, creates a summary, and performs a semantic search query based on the content.

### `similarity_top_k` (integer)

- The number of articles to return. Default is 9.

...
🧰 Tools
🪛 LanguageTool

[uncategorized] ~204-~204: A determiner appears to be missing. Consider inserting it.
Context: ...r): - The number of articles to return. Default is 9. ### ref (string): - The domain...

(AI_EN_LECTOR_MISSING_DETERMINER)


[uncategorized] ~217-~217: Loose punctuation mark.
Context: ...o retrieve articles. - "most_recent": Retrieves articles sorted by the most r...

(UNLIKELY_OPENING_PUNCTUATION)

🪛 Markdownlint

199-199: Expected: 1; Actual: 0; Below
Headings should be surrounded by blank lines

(MD022, blanks-around-headings)


203-203: Expected: 1; Actual: 0; Below
Headings should be surrounded by blank lines

(MD022, blanks-around-headings)


206-206: Expected: 1; Actual: 0; Below
Headings should be surrounded by blank lines

(MD022, blanks-around-headings)


210-210: Expected: 1; Actual: 0; Below
Headings should be surrounded by blank lines

(MD022, blanks-around-headings)


214-214: Expected: 1; Actual: 0; Below
Headings should be surrounded by blank lines

(MD022, blanks-around-headings)


197-197: null
Multiple headings with the same content

(MD024, no-duplicate-heading)


199-199: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


203-203: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


206-206: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


210-210: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


214-214: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


200-200: null
Lists should be surrounded by blank lines

(MD032, blanks-around-lists)


204-204: null
Lists should be surrounded by blank lines

(MD032, blanks-around-lists)


207-207: null
Lists should be surrounded by blank lines

(MD032, blanks-around-lists)


211-211: null
Lists should be surrounded by blank lines

(MD032, blanks-around-lists)


215-215: null
Lists should be surrounded by blank lines

(MD032, blanks-around-lists)


const (
BaseUrl = "https://api.dappier.com/app/datamodel"
SearchBaseUrl = "https://api.dappier.com/app/v2/search"
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consistency in Constant Naming

Consider renaming SearchBaseUrl to SearchBaseURL to maintain consistent camel casing with BaseUrl and improve code readability.

Comment on lines +19 to 25
// SearchRequest represents the request payload structure for the Dappier AI recommendations API
type SearchRequest struct {
Query string `json:"query"` // Natural language query or URL
SimilarityTopK int `json:"similarity_top_k"` // The number of articles to return (default is 9)
Ref string `json:"ref"` // Domain from which to fetch recommendations (e.g., techcrunch.com)
NumArticlesRef int `json:"num_articles_ref"` // Guaranteed number of articles from the specified domain
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Clarify the Purpose of SearchRequest Struct

The SearchRequest struct is introduced for the search functionality. However, it's currently being used in both the Search and AIRecommendations methods. To enhance code clarity and maintainability, consider creating separate structs for each API endpoint if their payloads differ or if they serve different purposes.

Comment on lines +171 to +187
return func(req *SearchRequest) {
req.SimilarityTopK = k
}
}

// WithRef sets the ref option
// The domain of the site from which the recommendations should come. For example, techcrunch.com.
func WithRef(ref string) RecommendationsOption {
return func(req *AiRecommendationsRequest) {
return func(req *SearchRequest) {
req.Ref = ref
}
}

// WithNumArticlesRef sets the num_articles_ref option
// Specifies how many articles should be guaranteed to match the domain specified in ref.
func WithNumArticlesRef(num int) RecommendationsOption {
return func(req *AiRecommendationsRequest) {
return func(req *SearchRequest) {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Unify Option Functions for Consistency

There are separate option functions for AIRecommendations (WithSimilarityTopK, WithRef, WithNumArticlesRef) and for Search (WithSearchSimilarityTopK, WithSearchRef, WithSearchNumArticlesRef). To improve usability, consider unifying these functions or clearly differentiating them.

Options:

  • Prefix all option functions with With and the API name, e.g., WithAIRecommendationsSimilarityTopK, WithSearchSimilarityTopK.
  • Alternatively, if the options are the same for both APIs, use a common set of option functions.

Also applies to: 280-298

Comment on lines +28 to +36
Status string `json:"status"`
Response SearchResult `json:"response"`
}

type SearchResult struct {
Query string `json:"query"`
Results []map[string]interface{} `json:"results"` // Dynamic JSON structure
Message string `json:"message"`
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Define Concrete Types for Search Results

Using map[string]interface{} for Results in SearchResult can lead to type assertions and potential runtime errors. Define a concrete struct for the search results to improve type safety and make the code more maintainable.

Example:

type SearchResult struct {
	Query   string        `json:"query"`
-	Results []map[string]interface{} `json:"results"` // Dynamic JSON structure
+	Results []SearchItem  `json:"results"`
	Message string        `json:"message"`
}

+type SearchItem struct {
+	Title          string  `json:"title"`
+	URL            string  `json:"url"`
+	Author         string  `json:"author"`
+	ImageURL       string  `json:"image_url"`
+	PreviewContent string  `json:"preview_content"`
+	PubDate        string  `json:"pubdate"`
+	PubDateUnix    int64   `json:"pubdate_unix"`
+	Score          float64 `json:"score"`
+	Site           string  `json:"site"`
+	SiteDomain     string  `json:"site_domain"`
+	// Add other relevant fields
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Status string `json:"status"`
Response SearchResult `json:"response"`
}
type SearchResult struct {
Query string `json:"query"`
Results []map[string]interface{} `json:"results"` // Dynamic JSON structure
Message string `json:"message"`
}
Status string `json:"status"`
Response SearchResult `json:"response"`
}
type SearchResult struct {
Query string `json:"query"`
Results []SearchItem `json:"results"`
Message string `json:"message"`
}
type SearchItem struct {
Title string `json:"title"`
URL string `json:"url"`
Author string `json:"author"`
ImageURL string `json:"image_url"`
PreviewContent string `json:"preview_content"`
PubDate string `json:"pubdate"`
PubDateUnix int64 `json:"pubdate_unix"`
Score float64 `json:"score"`
Site string `json:"site"`
SiteDomain string `json:"site_domain"`
// Add other relevant fields
}

Comment on lines +167 to +187
type RecommendationsOption func(*SearchRequest)

// WithSimilarityTopK sets the similarity_top_k option. The number of articles to return. Default is 9.
func WithSimilarityTopK(k int) RecommendationsOption {
return func(req *AiRecommendationsRequest) {
return func(req *SearchRequest) {
req.SimilarityTopK = k
}
}

// WithRef sets the ref option
// The domain of the site from which the recommendations should come. For example, techcrunch.com.
func WithRef(ref string) RecommendationsOption {
return func(req *AiRecommendationsRequest) {
return func(req *SearchRequest) {
req.Ref = ref
}
}

// WithNumArticlesRef sets the num_articles_ref option
// Specifies how many articles should be guaranteed to match the domain specified in ref.
func WithNumArticlesRef(num int) RecommendationsOption {
return func(req *AiRecommendationsRequest) {
return func(req *SearchRequest) {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Inconsistent Naming of Option Types and Functions

The option type RecommendationsOption and its associated functions (WithSimilarityTopK, WithRef, WithNumArticlesRef) are intended for AIRecommendations but are modifying SearchRequest. Similarly, new option types and functions are introduced for Search. This can cause confusion.

Consider the following:

  • Rename RecommendationsOption to SearchOption if it's modifying SearchRequest.
  • Ensure that option functions are clearly associated with their respective request types.

Example:

-type RecommendationsOption func(*SearchRequest)
+type RecommendationsOption func(*AiRecommendationsRequest)

And update the functions accordingly.

Committable suggestion was skipped due to low confidence.

Comment on lines +301 to +366
func (d *DappierApp) Search(query string, dataModelID string, opts ...SearchOption) (*SearchResponse, error) {
// Validate inputs
if query == "" || dataModelID == "" {
return nil, errors.New("query and data_model_id cannot be empty")
}

// Create the default request payload with the provided query
requestData := SearchRequest{
Query: query,
SimilarityTopK: 9, // default value
Ref: "", // default value
NumArticlesRef: 0, // default value
}

// Apply all optional parameters to the request data
for _, opt := range opts {
opt(&requestData)
}

// Marshal the request data into JSON
reqBody, err := json.Marshal(requestData)
if err != nil {
return nil, fmt.Errorf("failed to marshal request data: %w", err)
}

// Construct the request URL with the provided data model ID
url := fmt.Sprintf("%s?data_model_id=%s", SearchBaseUrl, dataModelID)

// Create a new HTTP request
req, err := http.NewRequest("POST", url, bytes.NewBuffer(reqBody))
if err != nil {
return nil, fmt.Errorf("failed to create new request: %w", err)
}

// Set necessary headers
req.Header.Set("Content-Type", ContentType)
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.APIKey))

// Use the client's HTTP client to make the request
resp, err := d.Client.Do(req)
if err != nil {
return nil, fmt.Errorf("error making HTTP request: %w", err)
}
defer resp.Body.Close()

// Check for non-OK status code
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("received non-OK response status: %d", resp.StatusCode)
}

// Read the response body using io.ReadAll
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}

// Unmarshal the response body into the SearchResponse structure
var result SearchResponse
err = json.Unmarshal(body, &result)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}

// Return the result
return &result, nil
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Check for Empty Results in Search Response

After unmarshalling the response in the Search method, there's no check to verify whether Results contains data. If the API returns an empty Results array, it might be appropriate to notify the caller.

Apply this diff to handle empty results:

// Unmarshal the response body into the SearchResponse structure
var result SearchResponse
err = json.Unmarshal(body, &result)
if err != nil {
	return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}

+// Check if results are available
+if len(result.Response.Results) == 0 {
+	return nil, errors.New("no search results found")
+}

return &result, nil
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (d *DappierApp) Search(query string, dataModelID string, opts ...SearchOption) (*SearchResponse, error) {
// Validate inputs
if query == "" || dataModelID == "" {
return nil, errors.New("query and data_model_id cannot be empty")
}
// Create the default request payload with the provided query
requestData := SearchRequest{
Query: query,
SimilarityTopK: 9, // default value
Ref: "", // default value
NumArticlesRef: 0, // default value
}
// Apply all optional parameters to the request data
for _, opt := range opts {
opt(&requestData)
}
// Marshal the request data into JSON
reqBody, err := json.Marshal(requestData)
if err != nil {
return nil, fmt.Errorf("failed to marshal request data: %w", err)
}
// Construct the request URL with the provided data model ID
url := fmt.Sprintf("%s?data_model_id=%s", SearchBaseUrl, dataModelID)
// Create a new HTTP request
req, err := http.NewRequest("POST", url, bytes.NewBuffer(reqBody))
if err != nil {
return nil, fmt.Errorf("failed to create new request: %w", err)
}
// Set necessary headers
req.Header.Set("Content-Type", ContentType)
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.APIKey))
// Use the client's HTTP client to make the request
resp, err := d.Client.Do(req)
if err != nil {
return nil, fmt.Errorf("error making HTTP request: %w", err)
}
defer resp.Body.Close()
// Check for non-OK status code
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("received non-OK response status: %d", resp.StatusCode)
}
// Read the response body using io.ReadAll
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}
// Unmarshal the response body into the SearchResponse structure
var result SearchResponse
err = json.Unmarshal(body, &result)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}
// Return the result
return &result, nil
}
func (d *DappierApp) Search(query string, dataModelID string, opts ...SearchOption) (*SearchResponse, error) {
// Validate inputs
if query == "" || dataModelID == "" {
return nil, errors.New("query and data_model_id cannot be empty")
}
// Create the default request payload with the provided query
requestData := SearchRequest{
Query: query,
SimilarityTopK: 9, // default value
Ref: "", // default value
NumArticlesRef: 0, // default value
}
// Apply all optional parameters to the request data
for _, opt := range opts {
opt(&requestData)
}
// Marshal the request data into JSON
reqBody, err := json.Marshal(requestData)
if err != nil {
return nil, fmt.Errorf("failed to marshal request data: %w", err)
}
// Construct the request URL with the provided data model ID
url := fmt.Sprintf("%s?data_model_id=%s", SearchBaseUrl, dataModelID)
// Create a new HTTP request
req, err := http.NewRequest("POST", url, bytes.NewBuffer(reqBody))
if err != nil {
return nil, fmt.Errorf("failed to create new request: %w", err)
}
// Set necessary headers
req.Header.Set("Content-Type", ContentType)
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", d.APIKey))
// Use the client's HTTP client to make the request
resp, err := d.Client.Do(req)
if err != nil {
return nil, fmt.Errorf("error making HTTP request: %w", err)
}
defer resp.Body.Close()
// Check for non-OK status code
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("received non-OK response status: %d", resp.StatusCode)
}
// Read the response body using io.ReadAll
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}
// Unmarshal the response body into the SearchResponse structure
var result SearchResponse
err = json.Unmarshal(body, &result)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal response: %w", err)
}
// Check if results are available
if len(result.Response.Results) == 0 {
return nil, errors.New("no search results found")
}
// Return the result
return &result, nil
}

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.

2 participants