@content-reviewer/core 0.0.1
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.
- package/README.md +86 -0
- package/dist/LICENSE +21 -0
- package/dist/index.d.mts +121 -0
- package/dist/index.d.ts +121 -0
- package/dist/index.js +393 -0
- package/dist/index.mjs +349 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# @content-reviewer/core
|
|
2
|
+
|
|
3
|
+
Library for reviewing written content using LLMs.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @content-reviewer/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { ContentReviewer, createReviewConfig } from '@content-reviewer/core';
|
|
15
|
+
import type { Document } from '@content-reviewer/core';
|
|
16
|
+
|
|
17
|
+
// 1. Configuration
|
|
18
|
+
const config = createReviewConfig({
|
|
19
|
+
language: 'ja',
|
|
20
|
+
llm: {
|
|
21
|
+
provider: 'openai',
|
|
22
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
23
|
+
model: 'gpt-4.1-mini',
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// 2. Document Preparation
|
|
28
|
+
const document: Document = {
|
|
29
|
+
rawContent: '# Article\n\nContent to review...',
|
|
30
|
+
source: 'article.md',
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// 3. Review
|
|
34
|
+
const reviewer = new ContentReviewer(config);
|
|
35
|
+
const result = await reviewer.review(document);
|
|
36
|
+
|
|
37
|
+
console.log(`Issues found: ${result.issues.length}`);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## API Reference
|
|
41
|
+
|
|
42
|
+
### `ContentReviewer`
|
|
43
|
+
|
|
44
|
+
The main class for performing reviews.
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
class ContentReviewer {
|
|
48
|
+
constructor(config: ReviewConfig);
|
|
49
|
+
review(document: Document): Promise<ReviewResult>;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### `createReviewConfig(input: ReviewConfigInput): ReviewConfig`
|
|
54
|
+
|
|
55
|
+
Creates a complete configuration object with default values.
|
|
56
|
+
|
|
57
|
+
### Types
|
|
58
|
+
|
|
59
|
+
#### `ReviewConfig`
|
|
60
|
+
|
|
61
|
+
Configuration for the reviewer, including LLM settings, language, and instructions.
|
|
62
|
+
|
|
63
|
+
#### `Document`
|
|
64
|
+
|
|
65
|
+
Represents the content to be reviewed.
|
|
66
|
+
|
|
67
|
+
- `rawContent`: The text content.
|
|
68
|
+
- `source`: Identifier (e.g., file path).
|
|
69
|
+
|
|
70
|
+
#### `ReviewResult`
|
|
71
|
+
|
|
72
|
+
The outcome of a review.
|
|
73
|
+
|
|
74
|
+
- `issues`: Array of `ReviewIssue` objects.
|
|
75
|
+
- `summary`: AI-generated summary of the review.
|
|
76
|
+
|
|
77
|
+
## Custom Instruction
|
|
78
|
+
|
|
79
|
+
Instructions (including persona and guidelines) can be provided as a string.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
const config = createReviewConfig({
|
|
83
|
+
instruction: 'You are a friendly editor. Check for typos and tone.',
|
|
84
|
+
// ...
|
|
85
|
+
});
|
|
86
|
+
```
|
package/dist/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 atkei
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
declare const reviewIssueSchema: z.ZodObject<{
|
|
4
|
+
severity: z.ZodEnum<{
|
|
5
|
+
error: "error";
|
|
6
|
+
warning: "warning";
|
|
7
|
+
suggestion: "suggestion";
|
|
8
|
+
}>;
|
|
9
|
+
message: z.ZodString;
|
|
10
|
+
matchText: z.ZodOptional<z.ZodString>;
|
|
11
|
+
lineNumber: z.ZodOptional<z.ZodNumber>;
|
|
12
|
+
suggestion: z.ZodOptional<z.ZodString>;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
declare const reviewResponseSchema: z.ZodObject<{
|
|
15
|
+
issues: z.ZodArray<z.ZodObject<{
|
|
16
|
+
severity: z.ZodEnum<{
|
|
17
|
+
error: "error";
|
|
18
|
+
warning: "warning";
|
|
19
|
+
suggestion: "suggestion";
|
|
20
|
+
}>;
|
|
21
|
+
message: z.ZodString;
|
|
22
|
+
matchText: z.ZodOptional<z.ZodString>;
|
|
23
|
+
lineNumber: z.ZodOptional<z.ZodNumber>;
|
|
24
|
+
suggestion: z.ZodOptional<z.ZodString>;
|
|
25
|
+
}, z.core.$strip>>;
|
|
26
|
+
summary: z.ZodString;
|
|
27
|
+
}, z.core.$strip>;
|
|
28
|
+
type ReviewIssueSchema = z.infer<typeof reviewIssueSchema>;
|
|
29
|
+
type ReviewResponseSchema = z.infer<typeof reviewResponseSchema>;
|
|
30
|
+
|
|
31
|
+
type Document = Readonly<{
|
|
32
|
+
rawContent: string;
|
|
33
|
+
source: string;
|
|
34
|
+
}>;
|
|
35
|
+
type Language = 'ja' | 'en';
|
|
36
|
+
type LLMProvider = 'openai' | 'anthropic' | 'google';
|
|
37
|
+
type LLMConfig = Readonly<{
|
|
38
|
+
provider: LLMProvider;
|
|
39
|
+
model: string;
|
|
40
|
+
apiKey?: string;
|
|
41
|
+
}>;
|
|
42
|
+
interface LLMClient {
|
|
43
|
+
generateReview(systemPrompt: string, userPrompt: string): Promise<ReviewResponseSchema>;
|
|
44
|
+
}
|
|
45
|
+
type ReviewConfig = Readonly<{
|
|
46
|
+
instruction?: string;
|
|
47
|
+
language: Language;
|
|
48
|
+
llm: LLMConfig;
|
|
49
|
+
}>;
|
|
50
|
+
type IssueSeverity = 'error' | 'warning' | 'suggestion';
|
|
51
|
+
type ReviewIssue = Readonly<{
|
|
52
|
+
severity: IssueSeverity;
|
|
53
|
+
message: string;
|
|
54
|
+
matchText?: string;
|
|
55
|
+
lineNumber?: number;
|
|
56
|
+
suggestion?: string;
|
|
57
|
+
}>;
|
|
58
|
+
type ReviewResult = Readonly<{
|
|
59
|
+
source: string;
|
|
60
|
+
issues: ReviewIssue[];
|
|
61
|
+
summary: string;
|
|
62
|
+
reviewedAt: Date;
|
|
63
|
+
}>;
|
|
64
|
+
|
|
65
|
+
type ReviewConfigInput = Readonly<{
|
|
66
|
+
instruction?: string;
|
|
67
|
+
language?: Language;
|
|
68
|
+
llm?: Partial<LLMConfig>;
|
|
69
|
+
}>;
|
|
70
|
+
declare const PROVIDER_DEFAULT_MODELS: Record<LLMProvider, string>;
|
|
71
|
+
declare const DEFAULT_LLM_CONFIG: LLMConfig;
|
|
72
|
+
declare const DEFAULT_CONFIG: ReviewConfig;
|
|
73
|
+
declare function createReviewConfig(input?: ReviewConfigInput): ReviewConfig;
|
|
74
|
+
declare function resolveApiKey(config: ReviewConfig): string;
|
|
75
|
+
declare function validateConfig(config: ReviewConfig): void;
|
|
76
|
+
|
|
77
|
+
declare class AISdkClient implements LLMClient {
|
|
78
|
+
private readonly config;
|
|
79
|
+
private readonly apiKey;
|
|
80
|
+
constructor(config: LLMConfig, apiKey: string);
|
|
81
|
+
generateReview(systemPrompt: string, userPrompt: string): Promise<ReviewResponseSchema>;
|
|
82
|
+
private createModel;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
declare function createLLMClient(config: LLMConfig, apiKey: string): LLMClient;
|
|
86
|
+
|
|
87
|
+
declare class ContentReviewer {
|
|
88
|
+
private readonly config;
|
|
89
|
+
constructor(config: ReviewConfig);
|
|
90
|
+
review(document: Document): Promise<ReviewResult>;
|
|
91
|
+
private runLLMReview;
|
|
92
|
+
private generateSummary;
|
|
93
|
+
private buildSystemPrompt;
|
|
94
|
+
private buildUserPrompt;
|
|
95
|
+
private findFirstMatchingLineNumber;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
declare const DEFAULT_INSTRUCTION_EN = "You are a professional editor and proofreader.\nPlease review the provided text for basic writing issues according to the following criteria:\n\n# Review Criteria\n\n## Grammar & Spelling\n- Correct typos and spelling errors.\n- Fix grammatical mistakes.\n\n## Clarity & Flow\n- Ensure sentences are clear and easy to read.\n- Point out ambiguous or confusing phrasing.\n\n## Consistency\n- Check for contradictions within the text.\n- Ensure consistent terminology and formatting.\n";
|
|
99
|
+
declare const DEFAULT_INSTRUCTION_JA = "\u3042\u306A\u305F\u306F\u30D7\u30ED\u306E\u7DE8\u96C6\u8005\u30FB\u6821\u6B63\u8005\u3067\u3059\u3002\n\u63D0\u4F9B\u3055\u308C\u305F\u30C6\u30AD\u30B9\u30C8\u3092\u3001\u4EE5\u4E0B\u306E\u57FA\u6E96\u306B\u5F93\u3063\u3066\u57FA\u672C\u7684\u306A\u6587\u7AE0\u306E\u554F\u984C\u70B9\u306B\u3064\u3044\u3066\u30EC\u30D3\u30E5\u30FC\u3057\u3066\u304F\u3060\u3055\u3044\uFF1A\n\n# \u30EC\u30D3\u30E5\u30FC\u57FA\u6E96\n\n## \u8AA4\u5B57\u8131\u5B57\u30FB\u6587\u6CD5\n- \u8AA4\u5B57\u3084\u8131\u5B57\u3092\u6307\u6458\u3057\u3066\u304F\u3060\u3055\u3044\u3002\n- \u6587\u6CD5\u7684\u306A\u8AA4\u308A\u3092\u4FEE\u6B63\u3057\u3066\u304F\u3060\u3055\u3044\u3002\n\n## \u308F\u304B\u308A\u3084\u3059\u3055\n- \u6587\u7AE0\u304C\u660E\u78BA\u3067\u8AAD\u307F\u3084\u3059\u3044\u304B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002\n- \u66D6\u6627\u306A\u8868\u73FE\u3084\u5206\u304B\u308A\u306B\u304F\u3044\u8A00\u3044\u56DE\u3057\u304C\u3042\u308C\u3070\u6307\u6458\u3057\u3066\u304F\u3060\u3055\u3044\u3002\n\n## \u4E00\u8CAB\u6027\u30FB\u77DB\u76FE\n- \u6587\u4E2D\u3067\u306E\u77DB\u76FE\u70B9\u304C\u306A\u3044\u304B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002\n- \u7528\u8A9E\u3084\u8868\u8A18\u306E\u63FA\u308C\uFF08\u4F8B\uFF1A\u300C\u30B3\u30F3\u30D4\u30E5\u30FC\u30BF\u300D\u3068\u300C\u30B3\u30F3\u30D4\u30E5\u30FC\u30BF\u30FC\u300D\u306E\u6DF7\u5728\u306A\u3069\uFF09\u3092\u6307\u6458\u3057\u3066\u304F\u3060\u3055\u3044\u3002\n";
|
|
100
|
+
|
|
101
|
+
declare class ContentReviewerError extends Error {
|
|
102
|
+
constructor(message: string);
|
|
103
|
+
}
|
|
104
|
+
declare class LLMError extends ContentReviewerError {
|
|
105
|
+
readonly cause?: unknown | undefined;
|
|
106
|
+
constructor(message: string, cause?: unknown | undefined);
|
|
107
|
+
}
|
|
108
|
+
declare class UnsupportedProviderError extends ContentReviewerError {
|
|
109
|
+
constructor(provider: string);
|
|
110
|
+
}
|
|
111
|
+
declare class MissingApiKeyError extends ContentReviewerError {
|
|
112
|
+
constructor(envVarName: string);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
declare const ENV_VARS: {
|
|
116
|
+
readonly OPENAI_API_KEY: "OPENAI_API_KEY";
|
|
117
|
+
readonly ANTHROPIC_API_KEY: "ANTHROPIC_API_KEY";
|
|
118
|
+
readonly GOOGLE_API_KEY: "GOOGLE_API_KEY";
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export { AISdkClient, ContentReviewer, ContentReviewerError, DEFAULT_CONFIG, DEFAULT_INSTRUCTION_EN, DEFAULT_INSTRUCTION_JA, DEFAULT_LLM_CONFIG, type Document, ENV_VARS, type IssueSeverity, type LLMClient, type LLMConfig, LLMError, type LLMProvider, type Language, MissingApiKeyError, PROVIDER_DEFAULT_MODELS, type ReviewConfig, type ReviewConfigInput, type ReviewIssue, type ReviewIssueSchema, type ReviewResponseSchema, type ReviewResult, UnsupportedProviderError, createLLMClient, createReviewConfig, resolveApiKey, reviewIssueSchema, reviewResponseSchema, validateConfig };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
declare const reviewIssueSchema: z.ZodObject<{
|
|
4
|
+
severity: z.ZodEnum<{
|
|
5
|
+
error: "error";
|
|
6
|
+
warning: "warning";
|
|
7
|
+
suggestion: "suggestion";
|
|
8
|
+
}>;
|
|
9
|
+
message: z.ZodString;
|
|
10
|
+
matchText: z.ZodOptional<z.ZodString>;
|
|
11
|
+
lineNumber: z.ZodOptional<z.ZodNumber>;
|
|
12
|
+
suggestion: z.ZodOptional<z.ZodString>;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
declare const reviewResponseSchema: z.ZodObject<{
|
|
15
|
+
issues: z.ZodArray<z.ZodObject<{
|
|
16
|
+
severity: z.ZodEnum<{
|
|
17
|
+
error: "error";
|
|
18
|
+
warning: "warning";
|
|
19
|
+
suggestion: "suggestion";
|
|
20
|
+
}>;
|
|
21
|
+
message: z.ZodString;
|
|
22
|
+
matchText: z.ZodOptional<z.ZodString>;
|
|
23
|
+
lineNumber: z.ZodOptional<z.ZodNumber>;
|
|
24
|
+
suggestion: z.ZodOptional<z.ZodString>;
|
|
25
|
+
}, z.core.$strip>>;
|
|
26
|
+
summary: z.ZodString;
|
|
27
|
+
}, z.core.$strip>;
|
|
28
|
+
type ReviewIssueSchema = z.infer<typeof reviewIssueSchema>;
|
|
29
|
+
type ReviewResponseSchema = z.infer<typeof reviewResponseSchema>;
|
|
30
|
+
|
|
31
|
+
type Document = Readonly<{
|
|
32
|
+
rawContent: string;
|
|
33
|
+
source: string;
|
|
34
|
+
}>;
|
|
35
|
+
type Language = 'ja' | 'en';
|
|
36
|
+
type LLMProvider = 'openai' | 'anthropic' | 'google';
|
|
37
|
+
type LLMConfig = Readonly<{
|
|
38
|
+
provider: LLMProvider;
|
|
39
|
+
model: string;
|
|
40
|
+
apiKey?: string;
|
|
41
|
+
}>;
|
|
42
|
+
interface LLMClient {
|
|
43
|
+
generateReview(systemPrompt: string, userPrompt: string): Promise<ReviewResponseSchema>;
|
|
44
|
+
}
|
|
45
|
+
type ReviewConfig = Readonly<{
|
|
46
|
+
instruction?: string;
|
|
47
|
+
language: Language;
|
|
48
|
+
llm: LLMConfig;
|
|
49
|
+
}>;
|
|
50
|
+
type IssueSeverity = 'error' | 'warning' | 'suggestion';
|
|
51
|
+
type ReviewIssue = Readonly<{
|
|
52
|
+
severity: IssueSeverity;
|
|
53
|
+
message: string;
|
|
54
|
+
matchText?: string;
|
|
55
|
+
lineNumber?: number;
|
|
56
|
+
suggestion?: string;
|
|
57
|
+
}>;
|
|
58
|
+
type ReviewResult = Readonly<{
|
|
59
|
+
source: string;
|
|
60
|
+
issues: ReviewIssue[];
|
|
61
|
+
summary: string;
|
|
62
|
+
reviewedAt: Date;
|
|
63
|
+
}>;
|
|
64
|
+
|
|
65
|
+
type ReviewConfigInput = Readonly<{
|
|
66
|
+
instruction?: string;
|
|
67
|
+
language?: Language;
|
|
68
|
+
llm?: Partial<LLMConfig>;
|
|
69
|
+
}>;
|
|
70
|
+
declare const PROVIDER_DEFAULT_MODELS: Record<LLMProvider, string>;
|
|
71
|
+
declare const DEFAULT_LLM_CONFIG: LLMConfig;
|
|
72
|
+
declare const DEFAULT_CONFIG: ReviewConfig;
|
|
73
|
+
declare function createReviewConfig(input?: ReviewConfigInput): ReviewConfig;
|
|
74
|
+
declare function resolveApiKey(config: ReviewConfig): string;
|
|
75
|
+
declare function validateConfig(config: ReviewConfig): void;
|
|
76
|
+
|
|
77
|
+
declare class AISdkClient implements LLMClient {
|
|
78
|
+
private readonly config;
|
|
79
|
+
private readonly apiKey;
|
|
80
|
+
constructor(config: LLMConfig, apiKey: string);
|
|
81
|
+
generateReview(systemPrompt: string, userPrompt: string): Promise<ReviewResponseSchema>;
|
|
82
|
+
private createModel;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
declare function createLLMClient(config: LLMConfig, apiKey: string): LLMClient;
|
|
86
|
+
|
|
87
|
+
declare class ContentReviewer {
|
|
88
|
+
private readonly config;
|
|
89
|
+
constructor(config: ReviewConfig);
|
|
90
|
+
review(document: Document): Promise<ReviewResult>;
|
|
91
|
+
private runLLMReview;
|
|
92
|
+
private generateSummary;
|
|
93
|
+
private buildSystemPrompt;
|
|
94
|
+
private buildUserPrompt;
|
|
95
|
+
private findFirstMatchingLineNumber;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
declare const DEFAULT_INSTRUCTION_EN = "You are a professional editor and proofreader.\nPlease review the provided text for basic writing issues according to the following criteria:\n\n# Review Criteria\n\n## Grammar & Spelling\n- Correct typos and spelling errors.\n- Fix grammatical mistakes.\n\n## Clarity & Flow\n- Ensure sentences are clear and easy to read.\n- Point out ambiguous or confusing phrasing.\n\n## Consistency\n- Check for contradictions within the text.\n- Ensure consistent terminology and formatting.\n";
|
|
99
|
+
declare const DEFAULT_INSTRUCTION_JA = "\u3042\u306A\u305F\u306F\u30D7\u30ED\u306E\u7DE8\u96C6\u8005\u30FB\u6821\u6B63\u8005\u3067\u3059\u3002\n\u63D0\u4F9B\u3055\u308C\u305F\u30C6\u30AD\u30B9\u30C8\u3092\u3001\u4EE5\u4E0B\u306E\u57FA\u6E96\u306B\u5F93\u3063\u3066\u57FA\u672C\u7684\u306A\u6587\u7AE0\u306E\u554F\u984C\u70B9\u306B\u3064\u3044\u3066\u30EC\u30D3\u30E5\u30FC\u3057\u3066\u304F\u3060\u3055\u3044\uFF1A\n\n# \u30EC\u30D3\u30E5\u30FC\u57FA\u6E96\n\n## \u8AA4\u5B57\u8131\u5B57\u30FB\u6587\u6CD5\n- \u8AA4\u5B57\u3084\u8131\u5B57\u3092\u6307\u6458\u3057\u3066\u304F\u3060\u3055\u3044\u3002\n- \u6587\u6CD5\u7684\u306A\u8AA4\u308A\u3092\u4FEE\u6B63\u3057\u3066\u304F\u3060\u3055\u3044\u3002\n\n## \u308F\u304B\u308A\u3084\u3059\u3055\n- \u6587\u7AE0\u304C\u660E\u78BA\u3067\u8AAD\u307F\u3084\u3059\u3044\u304B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002\n- \u66D6\u6627\u306A\u8868\u73FE\u3084\u5206\u304B\u308A\u306B\u304F\u3044\u8A00\u3044\u56DE\u3057\u304C\u3042\u308C\u3070\u6307\u6458\u3057\u3066\u304F\u3060\u3055\u3044\u3002\n\n## \u4E00\u8CAB\u6027\u30FB\u77DB\u76FE\n- \u6587\u4E2D\u3067\u306E\u77DB\u76FE\u70B9\u304C\u306A\u3044\u304B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002\n- \u7528\u8A9E\u3084\u8868\u8A18\u306E\u63FA\u308C\uFF08\u4F8B\uFF1A\u300C\u30B3\u30F3\u30D4\u30E5\u30FC\u30BF\u300D\u3068\u300C\u30B3\u30F3\u30D4\u30E5\u30FC\u30BF\u30FC\u300D\u306E\u6DF7\u5728\u306A\u3069\uFF09\u3092\u6307\u6458\u3057\u3066\u304F\u3060\u3055\u3044\u3002\n";
|
|
100
|
+
|
|
101
|
+
declare class ContentReviewerError extends Error {
|
|
102
|
+
constructor(message: string);
|
|
103
|
+
}
|
|
104
|
+
declare class LLMError extends ContentReviewerError {
|
|
105
|
+
readonly cause?: unknown | undefined;
|
|
106
|
+
constructor(message: string, cause?: unknown | undefined);
|
|
107
|
+
}
|
|
108
|
+
declare class UnsupportedProviderError extends ContentReviewerError {
|
|
109
|
+
constructor(provider: string);
|
|
110
|
+
}
|
|
111
|
+
declare class MissingApiKeyError extends ContentReviewerError {
|
|
112
|
+
constructor(envVarName: string);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
declare const ENV_VARS: {
|
|
116
|
+
readonly OPENAI_API_KEY: "OPENAI_API_KEY";
|
|
117
|
+
readonly ANTHROPIC_API_KEY: "ANTHROPIC_API_KEY";
|
|
118
|
+
readonly GOOGLE_API_KEY: "GOOGLE_API_KEY";
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export { AISdkClient, ContentReviewer, ContentReviewerError, DEFAULT_CONFIG, DEFAULT_INSTRUCTION_EN, DEFAULT_INSTRUCTION_JA, DEFAULT_LLM_CONFIG, type Document, ENV_VARS, type IssueSeverity, type LLMClient, type LLMConfig, LLMError, type LLMProvider, type Language, MissingApiKeyError, PROVIDER_DEFAULT_MODELS, type ReviewConfig, type ReviewConfigInput, type ReviewIssue, type ReviewIssueSchema, type ReviewResponseSchema, type ReviewResult, UnsupportedProviderError, createLLMClient, createReviewConfig, resolveApiKey, reviewIssueSchema, reviewResponseSchema, validateConfig };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AISdkClient: () => AISdkClient,
|
|
24
|
+
ContentReviewer: () => ContentReviewer,
|
|
25
|
+
ContentReviewerError: () => ContentReviewerError,
|
|
26
|
+
DEFAULT_CONFIG: () => DEFAULT_CONFIG,
|
|
27
|
+
DEFAULT_INSTRUCTION_EN: () => DEFAULT_INSTRUCTION_EN,
|
|
28
|
+
DEFAULT_INSTRUCTION_JA: () => DEFAULT_INSTRUCTION_JA,
|
|
29
|
+
DEFAULT_LLM_CONFIG: () => DEFAULT_LLM_CONFIG,
|
|
30
|
+
ENV_VARS: () => ENV_VARS,
|
|
31
|
+
LLMError: () => LLMError,
|
|
32
|
+
MissingApiKeyError: () => MissingApiKeyError,
|
|
33
|
+
PROVIDER_DEFAULT_MODELS: () => PROVIDER_DEFAULT_MODELS,
|
|
34
|
+
UnsupportedProviderError: () => UnsupportedProviderError,
|
|
35
|
+
createLLMClient: () => createLLMClient,
|
|
36
|
+
createReviewConfig: () => createReviewConfig,
|
|
37
|
+
resolveApiKey: () => resolveApiKey,
|
|
38
|
+
reviewIssueSchema: () => reviewIssueSchema,
|
|
39
|
+
reviewResponseSchema: () => reviewResponseSchema,
|
|
40
|
+
validateConfig: () => validateConfig
|
|
41
|
+
});
|
|
42
|
+
module.exports = __toCommonJS(index_exports);
|
|
43
|
+
|
|
44
|
+
// src/schemas.ts
|
|
45
|
+
var import_zod = require("zod");
|
|
46
|
+
var reviewIssueSchema = import_zod.z.object({
|
|
47
|
+
severity: import_zod.z.enum(["error", "warning", "suggestion"]),
|
|
48
|
+
message: import_zod.z.string(),
|
|
49
|
+
matchText: import_zod.z.string().optional(),
|
|
50
|
+
lineNumber: import_zod.z.number().optional(),
|
|
51
|
+
suggestion: import_zod.z.string().optional()
|
|
52
|
+
});
|
|
53
|
+
var reviewResponseSchema = import_zod.z.object({
|
|
54
|
+
issues: import_zod.z.array(reviewIssueSchema),
|
|
55
|
+
summary: import_zod.z.string()
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// src/constants.ts
|
|
59
|
+
var ENV_VARS = {
|
|
60
|
+
OPENAI_API_KEY: "OPENAI_API_KEY",
|
|
61
|
+
ANTHROPIC_API_KEY: "ANTHROPIC_API_KEY",
|
|
62
|
+
GOOGLE_API_KEY: "GOOGLE_API_KEY"
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// src/errors.ts
|
|
66
|
+
var ContentReviewerError = class extends Error {
|
|
67
|
+
constructor(message) {
|
|
68
|
+
super(message);
|
|
69
|
+
this.name = "ContentReviewerError";
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
var LLMError = class extends ContentReviewerError {
|
|
73
|
+
constructor(message, cause) {
|
|
74
|
+
super(message);
|
|
75
|
+
this.cause = cause;
|
|
76
|
+
this.name = "LLMError";
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
var UnsupportedProviderError = class extends ContentReviewerError {
|
|
80
|
+
constructor(provider) {
|
|
81
|
+
super(`Unsupported provider: ${provider}`);
|
|
82
|
+
this.name = "UnsupportedProviderError";
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
var MissingApiKeyError = class extends ContentReviewerError {
|
|
86
|
+
constructor(envVarName) {
|
|
87
|
+
super(
|
|
88
|
+
`API key not found. Please set ${envVarName} environment variable or provide it in the configuration.`
|
|
89
|
+
);
|
|
90
|
+
this.name = "MissingApiKeyError";
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// src/config.ts
|
|
95
|
+
var PROVIDER_DEFAULT_MODELS = {
|
|
96
|
+
openai: "gpt-4.1-mini",
|
|
97
|
+
anthropic: "claude-haiku-4-5",
|
|
98
|
+
google: "gemini-2.5-flash"
|
|
99
|
+
};
|
|
100
|
+
var DEFAULT_LLM_CONFIG = {
|
|
101
|
+
provider: "openai",
|
|
102
|
+
model: PROVIDER_DEFAULT_MODELS.openai
|
|
103
|
+
};
|
|
104
|
+
var DEFAULT_CONFIG = {
|
|
105
|
+
language: "en",
|
|
106
|
+
llm: DEFAULT_LLM_CONFIG
|
|
107
|
+
};
|
|
108
|
+
function createReviewConfig(input = {}) {
|
|
109
|
+
const provider = input.llm?.provider ?? DEFAULT_LLM_CONFIG.provider;
|
|
110
|
+
const model = input.llm?.model ?? PROVIDER_DEFAULT_MODELS[provider];
|
|
111
|
+
return {
|
|
112
|
+
instruction: input.instruction,
|
|
113
|
+
language: input.language ?? DEFAULT_CONFIG.language,
|
|
114
|
+
llm: {
|
|
115
|
+
provider,
|
|
116
|
+
model,
|
|
117
|
+
apiKey: input.llm?.apiKey
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function resolveApiKey(config) {
|
|
122
|
+
if (config.llm.apiKey) {
|
|
123
|
+
return config.llm.apiKey;
|
|
124
|
+
}
|
|
125
|
+
const envVarMap = {
|
|
126
|
+
openai: ENV_VARS.OPENAI_API_KEY,
|
|
127
|
+
anthropic: ENV_VARS.ANTHROPIC_API_KEY,
|
|
128
|
+
google: ENV_VARS.GOOGLE_API_KEY
|
|
129
|
+
};
|
|
130
|
+
const envVarName = envVarMap[config.llm.provider];
|
|
131
|
+
const envKey = process.env[envVarName];
|
|
132
|
+
if (!envKey) {
|
|
133
|
+
throw new MissingApiKeyError(envVarName);
|
|
134
|
+
}
|
|
135
|
+
return envKey;
|
|
136
|
+
}
|
|
137
|
+
function validateConfig(config) {
|
|
138
|
+
if (!config.language) {
|
|
139
|
+
throw new Error("language is required");
|
|
140
|
+
}
|
|
141
|
+
const validLanguages = ["ja", "en"];
|
|
142
|
+
if (!validLanguages.includes(config.language)) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
`Invalid language: ${config.language}. Supported languages are: ${validLanguages.join(", ")}`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
if (!config.llm.provider) {
|
|
148
|
+
throw new Error("LLM provider is required");
|
|
149
|
+
}
|
|
150
|
+
if (!config.llm.model) {
|
|
151
|
+
throw new Error("LLM model is required");
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// src/llm/ai-sdk-client.ts
|
|
156
|
+
var import_ai = require("ai");
|
|
157
|
+
var import_openai = require("@ai-sdk/openai");
|
|
158
|
+
var import_anthropic = require("@ai-sdk/anthropic");
|
|
159
|
+
var import_google = require("@ai-sdk/google");
|
|
160
|
+
var AISdkClient = class {
|
|
161
|
+
constructor(config, apiKey) {
|
|
162
|
+
this.config = config;
|
|
163
|
+
this.apiKey = apiKey;
|
|
164
|
+
}
|
|
165
|
+
async generateReview(systemPrompt, userPrompt) {
|
|
166
|
+
try {
|
|
167
|
+
const model = this.createModel();
|
|
168
|
+
const { object } = await (0, import_ai.generateObject)({
|
|
169
|
+
model,
|
|
170
|
+
schema: reviewResponseSchema,
|
|
171
|
+
system: systemPrompt,
|
|
172
|
+
prompt: userPrompt
|
|
173
|
+
});
|
|
174
|
+
return object;
|
|
175
|
+
} catch (error) {
|
|
176
|
+
if (error instanceof ContentReviewerError) {
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
if (error instanceof Error) {
|
|
180
|
+
throw new LLMError(`AI SDK request failed: ${error.message}`, error);
|
|
181
|
+
}
|
|
182
|
+
throw new LLMError("AI SDK request failed with unknown error", error);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
createModel() {
|
|
186
|
+
const { provider, model } = this.config;
|
|
187
|
+
switch (provider) {
|
|
188
|
+
case "openai": {
|
|
189
|
+
const openai = (0, import_openai.createOpenAI)({
|
|
190
|
+
apiKey: this.apiKey
|
|
191
|
+
});
|
|
192
|
+
return openai(model);
|
|
193
|
+
}
|
|
194
|
+
case "anthropic": {
|
|
195
|
+
const anthropic = (0, import_anthropic.createAnthropic)({
|
|
196
|
+
apiKey: this.apiKey
|
|
197
|
+
});
|
|
198
|
+
return anthropic(model);
|
|
199
|
+
}
|
|
200
|
+
case "google": {
|
|
201
|
+
const google = (0, import_google.createGoogleGenerativeAI)({
|
|
202
|
+
apiKey: this.apiKey
|
|
203
|
+
});
|
|
204
|
+
return google(model);
|
|
205
|
+
}
|
|
206
|
+
default:
|
|
207
|
+
throw new UnsupportedProviderError(provider);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// src/llm/index.ts
|
|
213
|
+
function createLLMClient(config, apiKey) {
|
|
214
|
+
return new AISdkClient(config, apiKey);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/default-instructions.ts
|
|
218
|
+
var DEFAULT_INSTRUCTION_EN = `You are a professional editor and proofreader.
|
|
219
|
+
Please review the provided text for basic writing issues according to the following criteria:
|
|
220
|
+
|
|
221
|
+
# Review Criteria
|
|
222
|
+
|
|
223
|
+
## Grammar & Spelling
|
|
224
|
+
- Correct typos and spelling errors.
|
|
225
|
+
- Fix grammatical mistakes.
|
|
226
|
+
|
|
227
|
+
## Clarity & Flow
|
|
228
|
+
- Ensure sentences are clear and easy to read.
|
|
229
|
+
- Point out ambiguous or confusing phrasing.
|
|
230
|
+
|
|
231
|
+
## Consistency
|
|
232
|
+
- Check for contradictions within the text.
|
|
233
|
+
- Ensure consistent terminology and formatting.
|
|
234
|
+
`;
|
|
235
|
+
var DEFAULT_INSTRUCTION_JA = `\u3042\u306A\u305F\u306F\u30D7\u30ED\u306E\u7DE8\u96C6\u8005\u30FB\u6821\u6B63\u8005\u3067\u3059\u3002
|
|
236
|
+
\u63D0\u4F9B\u3055\u308C\u305F\u30C6\u30AD\u30B9\u30C8\u3092\u3001\u4EE5\u4E0B\u306E\u57FA\u6E96\u306B\u5F93\u3063\u3066\u57FA\u672C\u7684\u306A\u6587\u7AE0\u306E\u554F\u984C\u70B9\u306B\u3064\u3044\u3066\u30EC\u30D3\u30E5\u30FC\u3057\u3066\u304F\u3060\u3055\u3044\uFF1A
|
|
237
|
+
|
|
238
|
+
# \u30EC\u30D3\u30E5\u30FC\u57FA\u6E96
|
|
239
|
+
|
|
240
|
+
## \u8AA4\u5B57\u8131\u5B57\u30FB\u6587\u6CD5
|
|
241
|
+
- \u8AA4\u5B57\u3084\u8131\u5B57\u3092\u6307\u6458\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
242
|
+
- \u6587\u6CD5\u7684\u306A\u8AA4\u308A\u3092\u4FEE\u6B63\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
243
|
+
|
|
244
|
+
## \u308F\u304B\u308A\u3084\u3059\u3055
|
|
245
|
+
- \u6587\u7AE0\u304C\u660E\u78BA\u3067\u8AAD\u307F\u3084\u3059\u3044\u304B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
246
|
+
- \u66D6\u6627\u306A\u8868\u73FE\u3084\u5206\u304B\u308A\u306B\u304F\u3044\u8A00\u3044\u56DE\u3057\u304C\u3042\u308C\u3070\u6307\u6458\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
247
|
+
|
|
248
|
+
## \u4E00\u8CAB\u6027\u30FB\u77DB\u76FE
|
|
249
|
+
- \u6587\u4E2D\u3067\u306E\u77DB\u76FE\u70B9\u304C\u306A\u3044\u304B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
250
|
+
- \u7528\u8A9E\u3084\u8868\u8A18\u306E\u63FA\u308C\uFF08\u4F8B\uFF1A\u300C\u30B3\u30F3\u30D4\u30E5\u30FC\u30BF\u300D\u3068\u300C\u30B3\u30F3\u30D4\u30E5\u30FC\u30BF\u30FC\u300D\u306E\u6DF7\u5728\u306A\u3069\uFF09\u3092\u6307\u6458\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
251
|
+
`;
|
|
252
|
+
|
|
253
|
+
// src/prompts.ts
|
|
254
|
+
var allPrompts = {
|
|
255
|
+
ja: {
|
|
256
|
+
generateSummary: (errorCount, warningCount) => `\u30EC\u30D3\u30E5\u30FC\u5B8C\u4E86\u3002\u30A8\u30E9\u30FC${errorCount}\u4EF6\u3001\u8B66\u544A${warningCount}\u4EF6\u3092\u691C\u51FA\u3002`,
|
|
257
|
+
buildSystemPrompt: ({ instruction }) => {
|
|
258
|
+
const instructions = instruction || DEFAULT_INSTRUCTION_JA;
|
|
259
|
+
return `${instructions}
|
|
260
|
+
\u30EC\u30D3\u30E5\u30FC\u7D50\u679C\u306F\u65E5\u672C\u8A9E\u3067\u3001\u4EE5\u4E0B\u306EJSON\u69CB\u9020\u3067\u8FD4\u3057\u3066\u304F\u3060\u3055\u3044\uFF1A
|
|
261
|
+
- issues: \u898B\u3064\u304B\u3063\u305F\u554F\u984C\u70B9\u306E\u914D\u5217
|
|
262
|
+
- severity: \u6DF1\u523B\u5EA6
|
|
263
|
+
- "error": \u81F4\u547D\u7684\u306A\u554F\u984C\uFF08\u4FEE\u6B63\u5FC5\u9808\uFF09
|
|
264
|
+
- "warning": \u91CD\u8981\u306A\u554F\u984C\uFF08\u4FEE\u6B63\u63A8\u5968\uFF09
|
|
265
|
+
- "suggestion": \u8EFD\u5FAE\u306A\u6539\u5584\u63D0\u6848\uFF08\u4EFB\u610F\uFF09
|
|
266
|
+
- message: \u554F\u984C\u306E\u8AAC\u660E
|
|
267
|
+
- matchText: \u554F\u984C\u7B87\u6240\u3092\u542B\u3080\u30C6\u30AD\u30B9\u30C8\u7247\uFF0810-50\u6587\u5B57\u7A0B\u5EA6\u3002\u5B8C\u5168\u4E00\u81F4\u3067\u304D\u308B\u56FA\u6709\u306E\u30C6\u30AD\u30B9\u30C8\u3092\u629C\u304D\u51FA\u3057\u3066\u304F\u3060\u3055\u3044\uFF09
|
|
268
|
+
- suggestion: \u6539\u5584\u63D0\u6848\uFF08\u30AA\u30D7\u30B7\u30E7\u30F3\uFF09
|
|
269
|
+
- summary: \u5168\u4F53\u7684\u306A\u7DCF\u8A55\uFF082-3\u6587\u7A0B\u5EA6\uFF09
|
|
270
|
+
|
|
271
|
+
\u6CE8\u610F\uFF1A
|
|
272
|
+
- lineNumber\u306F\u4E0D\u8981\u3067\u3059\u3002matchText\u306E\u307F\u3092\u63D0\u4F9B\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
273
|
+
- \u5EFA\u8A2D\u7684\u3067\u5177\u4F53\u7684\u306A\u30D5\u30A3\u30FC\u30C9\u30D0\u30C3\u30AF\u3092\u63D0\u4F9B\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
274
|
+
`;
|
|
275
|
+
},
|
|
276
|
+
buildUserPrompt: () => "\u4EE5\u4E0B\u306E\u8A18\u4E8B\u3092\u30EC\u30D3\u30E5\u30FC\u3057\u3066\u304F\u3060\u3055\u3044\uFF1A\n\n---\n"
|
|
277
|
+
},
|
|
278
|
+
en: {
|
|
279
|
+
generateSummary: (errorCount, warningCount) => `Review completed. Found ${errorCount} errors and ${warningCount} warnings.`,
|
|
280
|
+
buildSystemPrompt: ({ instruction }) => {
|
|
281
|
+
const instructions = instruction || DEFAULT_INSTRUCTION_EN;
|
|
282
|
+
return `${instructions}
|
|
283
|
+
Provide the review results in English with the following JSON structure:
|
|
284
|
+
- issues: Array of found issues
|
|
285
|
+
- severity: Severity level
|
|
286
|
+
- "error": Critical issues (Must fix)
|
|
287
|
+
- "warning": Important issues (Should fix)
|
|
288
|
+
- "suggestion": Minor suggestions (Nice to fix)
|
|
289
|
+
- message: Issue description
|
|
290
|
+
- matchText: Text snippet containing the issue (10-50 characters, extract unique text that can be exactly matched)
|
|
291
|
+
- suggestion: Improvement suggestion (optional)
|
|
292
|
+
- summary: Overall assessment (2-3 sentences)
|
|
293
|
+
|
|
294
|
+
Note:
|
|
295
|
+
- Do not provide lineNumber. Only provide matchText.
|
|
296
|
+
- Provide constructive and specific feedback.
|
|
297
|
+
`;
|
|
298
|
+
},
|
|
299
|
+
buildUserPrompt: () => "Please review the following article:\n\n---\n"
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
function getLanguagePrompts(language) {
|
|
303
|
+
if (language in allPrompts) {
|
|
304
|
+
return allPrompts[language];
|
|
305
|
+
}
|
|
306
|
+
throw new Error(`Unhandled language: ${language}`);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// src/reviewer.ts
|
|
310
|
+
var ContentReviewer = class {
|
|
311
|
+
constructor(config) {
|
|
312
|
+
this.config = config;
|
|
313
|
+
}
|
|
314
|
+
async review(document) {
|
|
315
|
+
const llmResult = await this.runLLMReview(document);
|
|
316
|
+
const summary = llmResult.summary || this.generateSummary(llmResult.issues);
|
|
317
|
+
return {
|
|
318
|
+
source: document.source,
|
|
319
|
+
issues: llmResult.issues,
|
|
320
|
+
summary,
|
|
321
|
+
reviewedAt: /* @__PURE__ */ new Date()
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
async runLLMReview(document) {
|
|
325
|
+
const apiKey = resolveApiKey(this.config);
|
|
326
|
+
const llmClient = createLLMClient(this.config.llm, apiKey);
|
|
327
|
+
const systemPrompt = this.buildSystemPrompt();
|
|
328
|
+
const userPrompt = this.buildUserPrompt(document);
|
|
329
|
+
const reviewData = await llmClient.generateReview(systemPrompt, userPrompt);
|
|
330
|
+
const issues = reviewData.issues.map((issue) => ({
|
|
331
|
+
...issue,
|
|
332
|
+
lineNumber: issue.matchText ? this.findFirstMatchingLineNumber(document.rawContent, issue.matchText) : void 0
|
|
333
|
+
}));
|
|
334
|
+
return {
|
|
335
|
+
issues,
|
|
336
|
+
summary: reviewData.summary
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
generateSummary(issues) {
|
|
340
|
+
const errorCount = issues.filter((i) => i.severity === "error").length;
|
|
341
|
+
const warningCount = issues.filter((i) => i.severity === "warning").length;
|
|
342
|
+
const { generateSummary } = getLanguagePrompts(this.config.language);
|
|
343
|
+
return generateSummary(errorCount, warningCount);
|
|
344
|
+
}
|
|
345
|
+
buildSystemPrompt() {
|
|
346
|
+
const { instruction, language } = this.config;
|
|
347
|
+
const { buildSystemPrompt } = getLanguagePrompts(language);
|
|
348
|
+
return buildSystemPrompt({ instruction });
|
|
349
|
+
}
|
|
350
|
+
buildUserPrompt(document) {
|
|
351
|
+
const { language } = this.config;
|
|
352
|
+
const { buildUserPrompt } = getLanguagePrompts(language);
|
|
353
|
+
const prompt = buildUserPrompt();
|
|
354
|
+
return prompt + document.rawContent + "\n---";
|
|
355
|
+
}
|
|
356
|
+
findFirstMatchingLineNumber(rawContent, matchText) {
|
|
357
|
+
const index = rawContent.indexOf(matchText);
|
|
358
|
+
if (index !== -1) {
|
|
359
|
+
const beforeMatch = rawContent.substring(0, index);
|
|
360
|
+
const lineNumber = (beforeMatch.match(/\n/g) || []).length + 1;
|
|
361
|
+
return lineNumber;
|
|
362
|
+
}
|
|
363
|
+
const lines = rawContent.split("\n");
|
|
364
|
+
const trimmedMatchText = matchText.trim();
|
|
365
|
+
for (let i = 0; i < lines.length; i++) {
|
|
366
|
+
if (lines[i].includes(trimmedMatchText)) {
|
|
367
|
+
return i + 1;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return void 0;
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
374
|
+
0 && (module.exports = {
|
|
375
|
+
AISdkClient,
|
|
376
|
+
ContentReviewer,
|
|
377
|
+
ContentReviewerError,
|
|
378
|
+
DEFAULT_CONFIG,
|
|
379
|
+
DEFAULT_INSTRUCTION_EN,
|
|
380
|
+
DEFAULT_INSTRUCTION_JA,
|
|
381
|
+
DEFAULT_LLM_CONFIG,
|
|
382
|
+
ENV_VARS,
|
|
383
|
+
LLMError,
|
|
384
|
+
MissingApiKeyError,
|
|
385
|
+
PROVIDER_DEFAULT_MODELS,
|
|
386
|
+
UnsupportedProviderError,
|
|
387
|
+
createLLMClient,
|
|
388
|
+
createReviewConfig,
|
|
389
|
+
resolveApiKey,
|
|
390
|
+
reviewIssueSchema,
|
|
391
|
+
reviewResponseSchema,
|
|
392
|
+
validateConfig
|
|
393
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
// src/schemas.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var reviewIssueSchema = z.object({
|
|
4
|
+
severity: z.enum(["error", "warning", "suggestion"]),
|
|
5
|
+
message: z.string(),
|
|
6
|
+
matchText: z.string().optional(),
|
|
7
|
+
lineNumber: z.number().optional(),
|
|
8
|
+
suggestion: z.string().optional()
|
|
9
|
+
});
|
|
10
|
+
var reviewResponseSchema = z.object({
|
|
11
|
+
issues: z.array(reviewIssueSchema),
|
|
12
|
+
summary: z.string()
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// src/constants.ts
|
|
16
|
+
var ENV_VARS = {
|
|
17
|
+
OPENAI_API_KEY: "OPENAI_API_KEY",
|
|
18
|
+
ANTHROPIC_API_KEY: "ANTHROPIC_API_KEY",
|
|
19
|
+
GOOGLE_API_KEY: "GOOGLE_API_KEY"
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// src/errors.ts
|
|
23
|
+
var ContentReviewerError = class extends Error {
|
|
24
|
+
constructor(message) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = "ContentReviewerError";
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
var LLMError = class extends ContentReviewerError {
|
|
30
|
+
constructor(message, cause) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.cause = cause;
|
|
33
|
+
this.name = "LLMError";
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var UnsupportedProviderError = class extends ContentReviewerError {
|
|
37
|
+
constructor(provider) {
|
|
38
|
+
super(`Unsupported provider: ${provider}`);
|
|
39
|
+
this.name = "UnsupportedProviderError";
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
var MissingApiKeyError = class extends ContentReviewerError {
|
|
43
|
+
constructor(envVarName) {
|
|
44
|
+
super(
|
|
45
|
+
`API key not found. Please set ${envVarName} environment variable or provide it in the configuration.`
|
|
46
|
+
);
|
|
47
|
+
this.name = "MissingApiKeyError";
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// src/config.ts
|
|
52
|
+
var PROVIDER_DEFAULT_MODELS = {
|
|
53
|
+
openai: "gpt-4.1-mini",
|
|
54
|
+
anthropic: "claude-haiku-4-5",
|
|
55
|
+
google: "gemini-2.5-flash"
|
|
56
|
+
};
|
|
57
|
+
var DEFAULT_LLM_CONFIG = {
|
|
58
|
+
provider: "openai",
|
|
59
|
+
model: PROVIDER_DEFAULT_MODELS.openai
|
|
60
|
+
};
|
|
61
|
+
var DEFAULT_CONFIG = {
|
|
62
|
+
language: "en",
|
|
63
|
+
llm: DEFAULT_LLM_CONFIG
|
|
64
|
+
};
|
|
65
|
+
function createReviewConfig(input = {}) {
|
|
66
|
+
const provider = input.llm?.provider ?? DEFAULT_LLM_CONFIG.provider;
|
|
67
|
+
const model = input.llm?.model ?? PROVIDER_DEFAULT_MODELS[provider];
|
|
68
|
+
return {
|
|
69
|
+
instruction: input.instruction,
|
|
70
|
+
language: input.language ?? DEFAULT_CONFIG.language,
|
|
71
|
+
llm: {
|
|
72
|
+
provider,
|
|
73
|
+
model,
|
|
74
|
+
apiKey: input.llm?.apiKey
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function resolveApiKey(config) {
|
|
79
|
+
if (config.llm.apiKey) {
|
|
80
|
+
return config.llm.apiKey;
|
|
81
|
+
}
|
|
82
|
+
const envVarMap = {
|
|
83
|
+
openai: ENV_VARS.OPENAI_API_KEY,
|
|
84
|
+
anthropic: ENV_VARS.ANTHROPIC_API_KEY,
|
|
85
|
+
google: ENV_VARS.GOOGLE_API_KEY
|
|
86
|
+
};
|
|
87
|
+
const envVarName = envVarMap[config.llm.provider];
|
|
88
|
+
const envKey = process.env[envVarName];
|
|
89
|
+
if (!envKey) {
|
|
90
|
+
throw new MissingApiKeyError(envVarName);
|
|
91
|
+
}
|
|
92
|
+
return envKey;
|
|
93
|
+
}
|
|
94
|
+
function validateConfig(config) {
|
|
95
|
+
if (!config.language) {
|
|
96
|
+
throw new Error("language is required");
|
|
97
|
+
}
|
|
98
|
+
const validLanguages = ["ja", "en"];
|
|
99
|
+
if (!validLanguages.includes(config.language)) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Invalid language: ${config.language}. Supported languages are: ${validLanguages.join(", ")}`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
if (!config.llm.provider) {
|
|
105
|
+
throw new Error("LLM provider is required");
|
|
106
|
+
}
|
|
107
|
+
if (!config.llm.model) {
|
|
108
|
+
throw new Error("LLM model is required");
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/llm/ai-sdk-client.ts
|
|
113
|
+
import { generateObject } from "ai";
|
|
114
|
+
import { createOpenAI } from "@ai-sdk/openai";
|
|
115
|
+
import { createAnthropic } from "@ai-sdk/anthropic";
|
|
116
|
+
import { createGoogleGenerativeAI } from "@ai-sdk/google";
|
|
117
|
+
var AISdkClient = class {
|
|
118
|
+
constructor(config, apiKey) {
|
|
119
|
+
this.config = config;
|
|
120
|
+
this.apiKey = apiKey;
|
|
121
|
+
}
|
|
122
|
+
async generateReview(systemPrompt, userPrompt) {
|
|
123
|
+
try {
|
|
124
|
+
const model = this.createModel();
|
|
125
|
+
const { object } = await generateObject({
|
|
126
|
+
model,
|
|
127
|
+
schema: reviewResponseSchema,
|
|
128
|
+
system: systemPrompt,
|
|
129
|
+
prompt: userPrompt
|
|
130
|
+
});
|
|
131
|
+
return object;
|
|
132
|
+
} catch (error) {
|
|
133
|
+
if (error instanceof ContentReviewerError) {
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
if (error instanceof Error) {
|
|
137
|
+
throw new LLMError(`AI SDK request failed: ${error.message}`, error);
|
|
138
|
+
}
|
|
139
|
+
throw new LLMError("AI SDK request failed with unknown error", error);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
createModel() {
|
|
143
|
+
const { provider, model } = this.config;
|
|
144
|
+
switch (provider) {
|
|
145
|
+
case "openai": {
|
|
146
|
+
const openai = createOpenAI({
|
|
147
|
+
apiKey: this.apiKey
|
|
148
|
+
});
|
|
149
|
+
return openai(model);
|
|
150
|
+
}
|
|
151
|
+
case "anthropic": {
|
|
152
|
+
const anthropic = createAnthropic({
|
|
153
|
+
apiKey: this.apiKey
|
|
154
|
+
});
|
|
155
|
+
return anthropic(model);
|
|
156
|
+
}
|
|
157
|
+
case "google": {
|
|
158
|
+
const google = createGoogleGenerativeAI({
|
|
159
|
+
apiKey: this.apiKey
|
|
160
|
+
});
|
|
161
|
+
return google(model);
|
|
162
|
+
}
|
|
163
|
+
default:
|
|
164
|
+
throw new UnsupportedProviderError(provider);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// src/llm/index.ts
|
|
170
|
+
function createLLMClient(config, apiKey) {
|
|
171
|
+
return new AISdkClient(config, apiKey);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// src/default-instructions.ts
|
|
175
|
+
var DEFAULT_INSTRUCTION_EN = `You are a professional editor and proofreader.
|
|
176
|
+
Please review the provided text for basic writing issues according to the following criteria:
|
|
177
|
+
|
|
178
|
+
# Review Criteria
|
|
179
|
+
|
|
180
|
+
## Grammar & Spelling
|
|
181
|
+
- Correct typos and spelling errors.
|
|
182
|
+
- Fix grammatical mistakes.
|
|
183
|
+
|
|
184
|
+
## Clarity & Flow
|
|
185
|
+
- Ensure sentences are clear and easy to read.
|
|
186
|
+
- Point out ambiguous or confusing phrasing.
|
|
187
|
+
|
|
188
|
+
## Consistency
|
|
189
|
+
- Check for contradictions within the text.
|
|
190
|
+
- Ensure consistent terminology and formatting.
|
|
191
|
+
`;
|
|
192
|
+
var DEFAULT_INSTRUCTION_JA = `\u3042\u306A\u305F\u306F\u30D7\u30ED\u306E\u7DE8\u96C6\u8005\u30FB\u6821\u6B63\u8005\u3067\u3059\u3002
|
|
193
|
+
\u63D0\u4F9B\u3055\u308C\u305F\u30C6\u30AD\u30B9\u30C8\u3092\u3001\u4EE5\u4E0B\u306E\u57FA\u6E96\u306B\u5F93\u3063\u3066\u57FA\u672C\u7684\u306A\u6587\u7AE0\u306E\u554F\u984C\u70B9\u306B\u3064\u3044\u3066\u30EC\u30D3\u30E5\u30FC\u3057\u3066\u304F\u3060\u3055\u3044\uFF1A
|
|
194
|
+
|
|
195
|
+
# \u30EC\u30D3\u30E5\u30FC\u57FA\u6E96
|
|
196
|
+
|
|
197
|
+
## \u8AA4\u5B57\u8131\u5B57\u30FB\u6587\u6CD5
|
|
198
|
+
- \u8AA4\u5B57\u3084\u8131\u5B57\u3092\u6307\u6458\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
199
|
+
- \u6587\u6CD5\u7684\u306A\u8AA4\u308A\u3092\u4FEE\u6B63\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
200
|
+
|
|
201
|
+
## \u308F\u304B\u308A\u3084\u3059\u3055
|
|
202
|
+
- \u6587\u7AE0\u304C\u660E\u78BA\u3067\u8AAD\u307F\u3084\u3059\u3044\u304B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
203
|
+
- \u66D6\u6627\u306A\u8868\u73FE\u3084\u5206\u304B\u308A\u306B\u304F\u3044\u8A00\u3044\u56DE\u3057\u304C\u3042\u308C\u3070\u6307\u6458\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
204
|
+
|
|
205
|
+
## \u4E00\u8CAB\u6027\u30FB\u77DB\u76FE
|
|
206
|
+
- \u6587\u4E2D\u3067\u306E\u77DB\u76FE\u70B9\u304C\u306A\u3044\u304B\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
207
|
+
- \u7528\u8A9E\u3084\u8868\u8A18\u306E\u63FA\u308C\uFF08\u4F8B\uFF1A\u300C\u30B3\u30F3\u30D4\u30E5\u30FC\u30BF\u300D\u3068\u300C\u30B3\u30F3\u30D4\u30E5\u30FC\u30BF\u30FC\u300D\u306E\u6DF7\u5728\u306A\u3069\uFF09\u3092\u6307\u6458\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
208
|
+
`;
|
|
209
|
+
|
|
210
|
+
// src/prompts.ts
|
|
211
|
+
var allPrompts = {
|
|
212
|
+
ja: {
|
|
213
|
+
generateSummary: (errorCount, warningCount) => `\u30EC\u30D3\u30E5\u30FC\u5B8C\u4E86\u3002\u30A8\u30E9\u30FC${errorCount}\u4EF6\u3001\u8B66\u544A${warningCount}\u4EF6\u3092\u691C\u51FA\u3002`,
|
|
214
|
+
buildSystemPrompt: ({ instruction }) => {
|
|
215
|
+
const instructions = instruction || DEFAULT_INSTRUCTION_JA;
|
|
216
|
+
return `${instructions}
|
|
217
|
+
\u30EC\u30D3\u30E5\u30FC\u7D50\u679C\u306F\u65E5\u672C\u8A9E\u3067\u3001\u4EE5\u4E0B\u306EJSON\u69CB\u9020\u3067\u8FD4\u3057\u3066\u304F\u3060\u3055\u3044\uFF1A
|
|
218
|
+
- issues: \u898B\u3064\u304B\u3063\u305F\u554F\u984C\u70B9\u306E\u914D\u5217
|
|
219
|
+
- severity: \u6DF1\u523B\u5EA6
|
|
220
|
+
- "error": \u81F4\u547D\u7684\u306A\u554F\u984C\uFF08\u4FEE\u6B63\u5FC5\u9808\uFF09
|
|
221
|
+
- "warning": \u91CD\u8981\u306A\u554F\u984C\uFF08\u4FEE\u6B63\u63A8\u5968\uFF09
|
|
222
|
+
- "suggestion": \u8EFD\u5FAE\u306A\u6539\u5584\u63D0\u6848\uFF08\u4EFB\u610F\uFF09
|
|
223
|
+
- message: \u554F\u984C\u306E\u8AAC\u660E
|
|
224
|
+
- matchText: \u554F\u984C\u7B87\u6240\u3092\u542B\u3080\u30C6\u30AD\u30B9\u30C8\u7247\uFF0810-50\u6587\u5B57\u7A0B\u5EA6\u3002\u5B8C\u5168\u4E00\u81F4\u3067\u304D\u308B\u56FA\u6709\u306E\u30C6\u30AD\u30B9\u30C8\u3092\u629C\u304D\u51FA\u3057\u3066\u304F\u3060\u3055\u3044\uFF09
|
|
225
|
+
- suggestion: \u6539\u5584\u63D0\u6848\uFF08\u30AA\u30D7\u30B7\u30E7\u30F3\uFF09
|
|
226
|
+
- summary: \u5168\u4F53\u7684\u306A\u7DCF\u8A55\uFF082-3\u6587\u7A0B\u5EA6\uFF09
|
|
227
|
+
|
|
228
|
+
\u6CE8\u610F\uFF1A
|
|
229
|
+
- lineNumber\u306F\u4E0D\u8981\u3067\u3059\u3002matchText\u306E\u307F\u3092\u63D0\u4F9B\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
230
|
+
- \u5EFA\u8A2D\u7684\u3067\u5177\u4F53\u7684\u306A\u30D5\u30A3\u30FC\u30C9\u30D0\u30C3\u30AF\u3092\u63D0\u4F9B\u3057\u3066\u304F\u3060\u3055\u3044\u3002
|
|
231
|
+
`;
|
|
232
|
+
},
|
|
233
|
+
buildUserPrompt: () => "\u4EE5\u4E0B\u306E\u8A18\u4E8B\u3092\u30EC\u30D3\u30E5\u30FC\u3057\u3066\u304F\u3060\u3055\u3044\uFF1A\n\n---\n"
|
|
234
|
+
},
|
|
235
|
+
en: {
|
|
236
|
+
generateSummary: (errorCount, warningCount) => `Review completed. Found ${errorCount} errors and ${warningCount} warnings.`,
|
|
237
|
+
buildSystemPrompt: ({ instruction }) => {
|
|
238
|
+
const instructions = instruction || DEFAULT_INSTRUCTION_EN;
|
|
239
|
+
return `${instructions}
|
|
240
|
+
Provide the review results in English with the following JSON structure:
|
|
241
|
+
- issues: Array of found issues
|
|
242
|
+
- severity: Severity level
|
|
243
|
+
- "error": Critical issues (Must fix)
|
|
244
|
+
- "warning": Important issues (Should fix)
|
|
245
|
+
- "suggestion": Minor suggestions (Nice to fix)
|
|
246
|
+
- message: Issue description
|
|
247
|
+
- matchText: Text snippet containing the issue (10-50 characters, extract unique text that can be exactly matched)
|
|
248
|
+
- suggestion: Improvement suggestion (optional)
|
|
249
|
+
- summary: Overall assessment (2-3 sentences)
|
|
250
|
+
|
|
251
|
+
Note:
|
|
252
|
+
- Do not provide lineNumber. Only provide matchText.
|
|
253
|
+
- Provide constructive and specific feedback.
|
|
254
|
+
`;
|
|
255
|
+
},
|
|
256
|
+
buildUserPrompt: () => "Please review the following article:\n\n---\n"
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
function getLanguagePrompts(language) {
|
|
260
|
+
if (language in allPrompts) {
|
|
261
|
+
return allPrompts[language];
|
|
262
|
+
}
|
|
263
|
+
throw new Error(`Unhandled language: ${language}`);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// src/reviewer.ts
|
|
267
|
+
var ContentReviewer = class {
|
|
268
|
+
constructor(config) {
|
|
269
|
+
this.config = config;
|
|
270
|
+
}
|
|
271
|
+
async review(document) {
|
|
272
|
+
const llmResult = await this.runLLMReview(document);
|
|
273
|
+
const summary = llmResult.summary || this.generateSummary(llmResult.issues);
|
|
274
|
+
return {
|
|
275
|
+
source: document.source,
|
|
276
|
+
issues: llmResult.issues,
|
|
277
|
+
summary,
|
|
278
|
+
reviewedAt: /* @__PURE__ */ new Date()
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
async runLLMReview(document) {
|
|
282
|
+
const apiKey = resolveApiKey(this.config);
|
|
283
|
+
const llmClient = createLLMClient(this.config.llm, apiKey);
|
|
284
|
+
const systemPrompt = this.buildSystemPrompt();
|
|
285
|
+
const userPrompt = this.buildUserPrompt(document);
|
|
286
|
+
const reviewData = await llmClient.generateReview(systemPrompt, userPrompt);
|
|
287
|
+
const issues = reviewData.issues.map((issue) => ({
|
|
288
|
+
...issue,
|
|
289
|
+
lineNumber: issue.matchText ? this.findFirstMatchingLineNumber(document.rawContent, issue.matchText) : void 0
|
|
290
|
+
}));
|
|
291
|
+
return {
|
|
292
|
+
issues,
|
|
293
|
+
summary: reviewData.summary
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
generateSummary(issues) {
|
|
297
|
+
const errorCount = issues.filter((i) => i.severity === "error").length;
|
|
298
|
+
const warningCount = issues.filter((i) => i.severity === "warning").length;
|
|
299
|
+
const { generateSummary } = getLanguagePrompts(this.config.language);
|
|
300
|
+
return generateSummary(errorCount, warningCount);
|
|
301
|
+
}
|
|
302
|
+
buildSystemPrompt() {
|
|
303
|
+
const { instruction, language } = this.config;
|
|
304
|
+
const { buildSystemPrompt } = getLanguagePrompts(language);
|
|
305
|
+
return buildSystemPrompt({ instruction });
|
|
306
|
+
}
|
|
307
|
+
buildUserPrompt(document) {
|
|
308
|
+
const { language } = this.config;
|
|
309
|
+
const { buildUserPrompt } = getLanguagePrompts(language);
|
|
310
|
+
const prompt = buildUserPrompt();
|
|
311
|
+
return prompt + document.rawContent + "\n---";
|
|
312
|
+
}
|
|
313
|
+
findFirstMatchingLineNumber(rawContent, matchText) {
|
|
314
|
+
const index = rawContent.indexOf(matchText);
|
|
315
|
+
if (index !== -1) {
|
|
316
|
+
const beforeMatch = rawContent.substring(0, index);
|
|
317
|
+
const lineNumber = (beforeMatch.match(/\n/g) || []).length + 1;
|
|
318
|
+
return lineNumber;
|
|
319
|
+
}
|
|
320
|
+
const lines = rawContent.split("\n");
|
|
321
|
+
const trimmedMatchText = matchText.trim();
|
|
322
|
+
for (let i = 0; i < lines.length; i++) {
|
|
323
|
+
if (lines[i].includes(trimmedMatchText)) {
|
|
324
|
+
return i + 1;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return void 0;
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
export {
|
|
331
|
+
AISdkClient,
|
|
332
|
+
ContentReviewer,
|
|
333
|
+
ContentReviewerError,
|
|
334
|
+
DEFAULT_CONFIG,
|
|
335
|
+
DEFAULT_INSTRUCTION_EN,
|
|
336
|
+
DEFAULT_INSTRUCTION_JA,
|
|
337
|
+
DEFAULT_LLM_CONFIG,
|
|
338
|
+
ENV_VARS,
|
|
339
|
+
LLMError,
|
|
340
|
+
MissingApiKeyError,
|
|
341
|
+
PROVIDER_DEFAULT_MODELS,
|
|
342
|
+
UnsupportedProviderError,
|
|
343
|
+
createLLMClient,
|
|
344
|
+
createReviewConfig,
|
|
345
|
+
resolveApiKey,
|
|
346
|
+
reviewIssueSchema,
|
|
347
|
+
reviewResponseSchema,
|
|
348
|
+
validateConfig
|
|
349
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@content-reviewer/core",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Library for reviewing written content using LLMs",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./dist/index.d.mts",
|
|
12
|
+
"default": "./dist/index.mjs"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean --tsconfig tsconfig.build.json",
|
|
25
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch --tsconfig tsconfig.build.json",
|
|
26
|
+
"test": "vitest run",
|
|
27
|
+
"test:watch": "vitest",
|
|
28
|
+
"clean": "rm -rf dist"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"ai",
|
|
32
|
+
"llm",
|
|
33
|
+
"markdown",
|
|
34
|
+
"proofreading",
|
|
35
|
+
"content-review"
|
|
36
|
+
],
|
|
37
|
+
"author": "atkei",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/atkei/content-reviewer.git",
|
|
42
|
+
"directory": "packages/core"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/atkei/content-reviewer#readme",
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/atkei/content-reviewer/issues"
|
|
47
|
+
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=20.0.0",
|
|
50
|
+
"pnpm": ">=9.0.0"
|
|
51
|
+
},
|
|
52
|
+
"sideEffects": false,
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"access": "public"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"@ai-sdk/anthropic": "^2.0.44",
|
|
58
|
+
"@ai-sdk/google": "^2.0.33",
|
|
59
|
+
"@ai-sdk/openai": "^2.0.67",
|
|
60
|
+
"ai": "^5.0.93",
|
|
61
|
+
"axios": "^1.13.2",
|
|
62
|
+
"zod": "^4.1.12"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@types/node": "^24.10.1",
|
|
66
|
+
"tsup": "^8.5.1",
|
|
67
|
+
"typescript": "^5.9.3",
|
|
68
|
+
"vitest": "^4.0.9"
|
|
69
|
+
}
|
|
70
|
+
}
|